Tomcat——Jar包加載機制


 

 

問題描述

最近在升級Tomcat,我們有兩個絕對相同的服務器(硬件和操作系統)。兩者都運行Jdk_1.8.0_191,一台安裝了 tomcat_7.x,一台安裝了 tomcat_8.5.60。

啟動和VM參數以及特定於應用程序的設置完全相同。唯一的區別是IP地址,當然還有主機名。

下面情況都是相同war包相同Tomcat情況下:

系統 Tomcat版本 能否啟動
Windows Tomcat7
Windows Tomcat8
Linux Tomcat7
Linux Tomcat8 不能

 

Tomcat7加載Jar包原理

Tomcat自己實現了自己的類加載器,用於加載自己本地項目中jar包中的所有class文件,所以在相同的類加載器下,

如果有相同路徑名和類名那么加載順序就是根據jar包的順序來決定的。誰的jar包先進來,那么就先加載哪個類。

但是為什么在Tomcat7所有環境都能運行正常,而在Tomcat8中就不行了呢? 於是就查看了Tomcat7的源碼在Context加載項目中的jar包時

Tomcat7加載jar部分,在WebappLoader.setRepositories()方法中,粘貼出其中重要代碼。

 // Looking up directory /WEB-INF/lib in the context
    NamingEnumeration<NameClassPair> enumeration = null;
    try {
        //這一句是獲得jar包的路徑
        enumeration = libDir.list("");
    } catch (NamingException e) {
        IOException ioe = new IOException(sm.getString(
                "webappLoader.namingFailure", libPath));
        ioe.initCause(e);
        throw ioe;
    }

list是獲得了應用中WEB-INF下lib下所有jar包的路徑。我們跟蹤進去發現FileDirContext 的list方法中有下面這一句:

Arrays.sort(names);             // Sort alphabetically

我們可以發現在Tomcat7中對獲得所有jar包作了一個排序的動作。對jar包進行了首字母a-z進行了排序。

而我們所期望加載的那個jar包首字母正好在錯誤jar包的前面。

 

Tomcat8加載Jar包原理

上面我們知道了為什么在所有項目中Tomcat7能啟動起來的原因了,是因為Tomcat7做了排序的動作,那么在Tomcat8加載Jar包時,又是怎么做的呢?

Tomcat8在加載源碼的時候是通過StandardRoot.processWebInfLib()方法進行加載的

    protected void processWebInfLib() throws LifecycleException {
        WebResource[] possibleJars = listResources("/WEB-INF/lib", false);
        for (WebResource possibleJar : possibleJars) {
            if (possibleJar.isFile() && possibleJar.getName().endsWith(".jar")) {
                createWebResourceSet(ResourceSetType.CLASSES_JAR,
                        "/WEB-INF/classes", possibleJar.getURL(), "/");
            }
        }
    }

在這我們可以看到Tomcat沒有對取出來的Jar作任何動作,僅僅是File file = new File()這樣遍歷出來了。

那么為什么相同的Tomcat8相同的War包在Windos能啟動起來,但是在Linux中都啟動不起來呢?

經過試驗發現Java的獲取文件夾下面的所有文件是跟操作系統的文件系統有關系的,相同的文件夾內容,在Windows中取出來,輸出名字你會發現輸出是經過a-z排序過的,

但是在Linux中你可以根據命令ll -fi就可以輸出自然順序,你會發現沒有什么規律可言。

 

解決

到這里上面描述的所有問題我們都能解釋通了,接下來就該如何解決了。

  1. 修改Tomcat8的源碼,在獲取所有Jar包的時候,也對它進行排序
  2. 解決掉有沖突的文件,使用統一的版本。以下命令可以找到重復的jar包引用:
mvn dependency:tree -Dverbose -Dincludes=io.netty

第一種解決辦法只能解決一時問題,即項目能正常啟動起來,但是一旦隨后涉及到了相關類的修改,那么沖突類的哪個類呢?那么這個問題肯定是一個定時炸彈。

第二種方案是找到有沖突的文件,然后找出不用的那個給刪除掉,但是發現刪除一個又會蹦出其他的,刪除了好幾個以后發現由於買的項目代碼不規范,

所以這種現象特別多,如果單純靠手工篩選的話極其麻煩。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM