Tomcat的Classpath-常见问题以及如何解决
在很多Apache Tomcat用戶論壇,一個問題經常被提出,那就是如何配置Tomcat的classpath,使得一個web應用程序能夠找到類或者jar文件,從而可以正常工作。 就像許多困擾Tomcat新用戶的問題一樣,這個問題也很容易解決。 在這篇文章中,我們將會介紹Tomcat是如何產生和利用classpath的,然后一個一個解決大多數(shù)常見的與classpath相關的問題。
為什么Classpaths給Tomcat用戶帶來了麻煩
一個classpath就是一個參數(shù),來告訴java虛擬機在哪里可以找到類和包去運行一個程序。classpath總是在程序源碼外設置的,將其同程序分開可以允許java代碼以一種抽象的方式來引用類和包,允許程序可以在任何系統(tǒng)上被配置。 為什么那些很有經驗的java用戶,他們已經非常清楚classpath是什么了,但是在Tomcat運行程序的時候,還是會遇到這樣那樣的問題呢?
可能有以下三個原因:
1、Tomcat處理classpaths的方式與其他java程序是不同的
2、Tomcat的版本不同,其處理classpaths的方式也可能不同
3、Tomcat的文檔和默認的配置要求以一種特定的方式來完成某些事情。如果你不遵循這種方式,那么你就會陷入麻煩之中。關于如何解決常見的classpath問題,沒有信息可以提供,比如外部依賴,共享依賴或者多版本的相同依賴。
Tomcat的Classpath如何不同于標準的Classpath
Apache Tomcat目的是盡可能的獨立,直觀和自動化,為了有效的管理,標準化web應用程序的配置和部署過程,為了安全和命名控件的考慮,限制對不同庫的訪問。 這就是為什么不使用java classpath環(huán)境變量的原因了,java的classpath是聲明依賴類庫默認的地方,Tomcat的start腳本忽略了這個變量,在創(chuàng)建Tomcat系統(tǒng)classloader的時候產生了自己的classpaths。
想要理解Tomcat如何處理classpath的,看看以下Tomcat6的啟動過程:
1、java虛擬機的bootstrap loader加載java的核心類庫。java虛擬機使用JAVA_HOME環(huán)境變量來定位核心庫的位置。
2、Startup.sh,使用start參數(shù)調用Catalina.sh,重寫系統(tǒng)的classpath并加載bootstrap.jar和tomcat-juli.jar。這些資源僅對Tomcat可見。
3、為每一個部署的Context創(chuàng)建Class loader,加載位于每個web應用程序WEB-INF/classes和WEB-INF/lib目錄下的所有類和jar文件。每個web應用程序僅僅可見自己目錄下的資源。
4、Common class loader加載位于$CATALINA_HOME/lib目錄下的所有類和jar文件,這些資源對所有應用程序和Tomcat可見。
Tomcat的類加載機制是如何在不同版本之間變化的
之前的Tomcat版本,其類加載機制有一些不同
Tomcat 4.x以及更早的版本,一個server loader負責加載Catalina類,現(xiàn)在由commons loader負責了。
Tomcat 5.x,一個shared loader負責加載在應用程序間共享的類,這些類位于$CATALINA_HOME/shared/lib。在Tomcat6中,這種方式被取消了。
Tomcat 5.x也包括了一個Catalina loader,加載所有的Catalina組件,現(xiàn)在也被Common loader取代了。
當你不能按照Tomcat要求的方式做事的時候,怎么辦
如果你使用Tomcat文檔推薦的方式做事,你不應該有關于classpath的問題。 你的wars包含了所有庫和包的復本,你沒有任何理由去在多個應用程序間共享一個jar文件,你不需要調用任何外在的資源,你也將不會遇到復雜的情況,例如一個web應用程序運行的時候需要一個jar文件的多個版本。 但是如果你確實不能按照推薦的方式來做的時候,一個文件可以解決你所有的問題:catalina.properties。
使用catalina.properties來配置Tomcat Classpath
對于那些不想使用默認來加載方式的用戶來說,幸運的是,Tomcat的classpath選項不是硬編碼的,它們是從$CATALINA_HOME/conf/catalina.properties文件中讀取的。
這個文件包含了除bootstrap和system loader之外的所有其他的loaders,檢查這個文件,你會有一些新發(fā)現(xiàn):
1、Server以及Shared loader還沒有被刪除,如果它們的屬性沒有定義,Commons loader負責處理。
2、被各種loaders加載的類和jar文件不會被自動加載,它們只是用一個簡單的通配符語法指定為一組。
3、這里沒有說你不能指定一個額外的倉庫,事實上就是說你是可以的。
server loader不應該改動,但是shared loader還是有許多有用的應用。(shared loader將會在啟動過程的最后階段加載它的類,在Commons loader之后)。 現(xiàn)在讓我們看看一些常見的問題以及如何解決。
問題、解決方案和最佳實踐
問題:我的應用程序依賴一個外部的倉庫,我不能引用它。
讓Tomcat知道一個外部的倉庫,在catalina.properties文件的shared loader位置,使用正確的語法,聲明這個倉庫。語法基于你要配置的文件或倉庫的類型:
1、增加一個文件夾作為類倉庫,使用“path/to/foldername”
2、增加一個文件夾下的所有jar文件作為類倉庫,使用"path/to/foldername/*.jar"
3、增加單個jar文件作為類倉庫,使用"file://path/to/foldername/jarname.jar"
4、調用環(huán)境變量,使用格式,例如{}格式,例如格式,例如{VARIABLE_NAME}
5、聲明多個資源,用逗號分隔開
6、所有的路徑相對于CATALINA_BASE或CATALINA_HOME,或者是絕對路徑
問題:我想多個應用程序共享一個jar文件,這個jar文件在Tomcat里面。
除了一些常見的第三方庫(比如JDBC drivers),最好不要在$CATALINA_HOME/lib目錄下包含額外的庫,即使這樣在一些情況下是可行的。應該重新創(chuàng)建比如/shared/lib和/shared/classes目錄,然后在catalina.properties配置shared.loader屬性:
“shared/classes,shared/lib/*.jar”
問題:除了另一個框架,我在一個應用中使用了一個嵌入式Tomcat server,當我訪問框架組件的時候遇到classpath errors。
這個問題好像超出了這篇文章的范疇,但是作為一個常見的classpath相關的問題,這里對如何引起你的錯誤作一個簡單的介紹。
當嵌入到包含另外核心框架(Wicket或者Spring)的應用中時,Tomcat將在啟動框架的時候,使用System classloader加載核心類,而不是從應用的WEB-INF/lib目錄下加載。
java的類加載是懶加載,就是說請求某個類的第一個加載器擁有這個類剩下的生命周期。如果System classloader,它的類對web應用是不可見的,首先加載了框架相關的類,java虛擬機將會阻止來的其他實例被創(chuàng)建,這樣就引起了classpath錯誤。
解決這個問題的一種方式就是增加一個自定義的bootstrap classloader,使得這個classloader加載合適的庫,然后對程序剩下部分的啟動正常對待。
問題:我使用一個標準的應用程序,程序的war包含了依賴的所有包,但是我仍然遇到類定義錯誤。
這個問題可能是由許多事情引起的,包括編譯或部署過程不是很正確,但是最有可能是web應用程序的目錄結構不對造成的。
java命名轉換要求類名映射到存放這個類的目錄結構。例如,一個類com.mycompany.mygreat.class需要被存放到目錄WEB-INF/classes/com/mycompany/。
經常代碼中丟失一個點號就會引起classpath相關的問題。
問題:我的web應用程序的不同模塊需要使用同一個jar包的兩個不同版本。
這種情況常常出現(xiàn)在一個應用程序中使用多個web框架,這些web框架依賴一個庫的不同版本。
有幾種解決方案,但是它們都和classpath不相關。 我們在這里說這個問題,是因為一些用戶試圖在框架的jar文件中的Manifest文件中指定依賴的庫的不同版本的classpath,來解決這個問題。
這種方式在一些web應用服務器中是支持的,所以一些用戶想要在Tomcat中也使用這種方式。但是在Manifest文件中指定classpath在Tomcat中是不支持的,這也不是Servlet說明的一部分。
有以下四種方式來解決這個問題:
1、你可以更新框架的版本,如果這里能夠使得與其他框架依賴相同的版本。
2、你可以創(chuàng)建兩個或更多的自定義classloaders,每個jar文件一個,在WEB/INF/context.xml文件中配置,創(chuàng)建你所需要的兩個不同版本的類的實例。
3、你可以使用jarjar工具將框架和它依賴的庫打包成一個jar文件,那么它們會一起被加載。
4、如果你發(fā)現(xiàn)你每隔一天就要處理這種情況,你應該考慮使用OSGI框架,這個框架有許多方法專門就是用來處理這種一個類的多個版本需要運行在同一個java虛擬機里的情況的。
最佳實踐
1、避免在Tomcat里使用Commons loader加載不屬于Tomcat標準發(fā)布的庫和包,這可能會引起兼容錯誤。如果你需要在多個應用程序間共享一個庫或包,創(chuàng)建shared/lib和shared/classes目錄,然后配置到catalina.properties文件的Shared loader。
2、以上規(guī)則的一個例外就是常用的第三方共享庫,例如JDBC driver。這些應該放到$CATALINA_HOME/lib目錄下。
3、可能的話,盡可能按照Tomcat的開發(fā)者推薦的方式使用Tomcat,如果你發(fā)現(xiàn)你不得不頻繁配置classpath,你可能需要重新考慮你的開發(fā)過程了。
總結
以上是生活随笔為你收集整理的Tomcat的Classpath-常见问题以及如何解决的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP 方法:GET 对比 POST
- 下一篇: 职场小白技能