Spring Boot 項目本地運行無異常,部署到 Linux 服務器運行報錯:java.lang.ClassNotFoundException

一 背景

最近在用 Springboot 開發項目 A,引了小夥伴開發的模塊 B,本地起服務,運行的好好的,等部署到服務器上,一運行就報錯:Caused by: java.lang.ClassNotFoundException。html

注:致使該錯誤的緣由有不少,好比:包衝突、類衝突、包不存在等,我這裏只列舉其中一種狀況,畢竟坑了我半天的時間,我以爲有必要分享出來。java

二 緣由

先看一個詭異的現象吧,我用別的工程 C 引用了一樣的依賴 B,部署到服務器上,運行的好好的,就我這個工程不行?細看:apache

1)工程 C 部署到服務器上,/app/C-service/lib 下的 jar包是:api

B-api-1.0.0-20191224.024308-5.jar
B-dao-1.0.0-20191223.073120-2.jar

2)工程 C 部署到服務器上,/app/C-service/C-service.jar/META-INF/MANIFEST.MF,掃描路徑是:服務器

lib/B-api-1.0.0-20191224.024308-5.jar
lib/B-dao-1.0.0-20191223.073120-2.jar

而:app

1)個人工程 A 部署到服務器上,/app/A-service/lib 下的 jar包是:maven

B-api-1.0.0-SNAPSHOT.jar
B-dao-1.0.0-SNAPSHOT.jar

2)個人工程 A 部署到服務器上,/app/A-service/A-service.jar/META-INF/MANIFEST.MF,掃描路徑是:ide

lib/B-api-1.0.0-20191224.024308-5.jar
lib/B-dao-1.0.0-20191223.073120-2.jar

發現:MANIFEST.MF 中掃描的 jar 包路徑和實際解壓出來的 jar 包名稱不一致(一個帶時間戳,一個是 SNAPSHOT),因此掃描不到 jar 包,致使報錯:Caused by: java.lang.ClassNotFoundException。spa

那麼是什麼緣由構成了這樁慘案呢?機緣巧合:code

1.我小夥伴的模塊,deploy 時用的是 SNAPSHOT,如 1.0.0-SNAPSHOT,我在拉取 jar包時,拉到的是:B-api-1.0.0-20191224.024308-5.jar:

<dependency>
    <groupId>com.xxx.xxx</groupId>
    <artifactId>B-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

2.個人工程 A 本地能夠跑,是由於那時候我沒有打包 mvn clean package -Dmaven.test.skip=true,IDEA 本地運行項目時 jar 包掃描路徑 和 實際拉取的 jar 包一致;當我打包後,解壓出 A-service.zip 包,本地運行一樣會報錯;

3.問題在於工程 A 打包先後的區別,爲何打包後:MANIFEST.MF 和 A-service.zip 解壓後的 lib/ 目錄下的 jar包 名稱不一致?

4.最後經過比較工程 C 的配置文件和 A 的配置文件,找到不一樣點:A工程的 assembly.xml 中多了一行:<outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>

<dependencySets>
    <dependencySet>
       <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>
        <useProjectArtifact>false</useProjectArtifact>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
</dependencySets>

5.把這行去掉,問題確實解決了。可是爲何呢?拉下來的 jar 包、MANIFEST.MF 中掃描的 jar包帶時間戳,總以爲不夠清秀、哪裏不對勁。隨後我以 outputFileNameMapping 爲字眼找了下相關資料,果真找到了另一位老大哥:<useUniqueVersions>false</useUniqueVersions> ,這行配置是放在工程的 Service 模塊(Application.java 啓動類所在模塊)的 pom.xml 中:

<!-- 打包jar文件時,配置manifest文件,加入lib包的jar依賴 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>${main.class}</mainClass>
                <useUniqueVersions>false</useUniqueVersions>
            </manifest>
        </archive>
        <excludes>
            <exclude>*.yml</exclude>
            <exclude>*.properties</exclude>
        </excludes>
    </configuration>
</plugin>

6.解釋:

1)<outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>,做用是在你打包的時候:lib/ 下的 jar 包能夠重命名,好比原本應該是 B-api-1.0.0-20191224.024308-5.jar,經過這行配置後,jar包被重命名爲:B-api-1.0.0-SNAPSHOT.jar;

2)<useUniqueVersions>false</useUniqueVersions>,做用是在你生成 MANIFEST.MF 文件時,lib/ 下的 jar包若是版本是 xxx-SNAPSHOT,要不要帶惟一版本時間戳,若是你配置爲 false(默認爲 true),那麼原本應該是 B-api-1.0.0-20191224.024308-5.jar,經過這行配置後,jar包被重命名爲:B-api-1.0.0-SNAPSHOT.jar;

恰好:

1)工程 C 中 outputFileNameMappinguseUniqueVersions 都沒配置,使用默認值,使得 lib/ 下的 jar包名稱 和 MANIFEST.MF 下的掃描路徑 jar包名稱一致(都帶時間戳);

2)而個人工程 A,不僅從哪裏拷貝來的配置文件,配了 outputFileNameMapping (沒帶時間戳)但沒配置 useUniqueVersions(帶時間戳)致使不一致;

因此:這兩個配置,要麼都有,要麼都沒有,否則就會出現不一致,致使報錯。

三 解決

我我的以爲,jar 包名稱仍是不帶時間戳更清秀些,因此我推薦 outputFileNameMappinguseUniqueVersions 都配置上,以下:

1.assembly.xml 中添加配置:<outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>

<dependencySets>
    <dependencySet>
        <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}.${artifact.extension}</outputFileNameMapping>
        <useProjectArtifact>false</useProjectArtifact>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
</dependencySets>

2.啓動類 Application.java 所在模塊的 pom.xml 中添加配置:<useUniqueVersions>false</useUniqueVersions>

<!-- 打包jar文件時,配置manifest文件,加入lib包的jar依賴 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>${main.class}</mainClass>
                <useUniqueVersions>false</useUniqueVersions>
            </manifest>
        </archive>
        <excludes>
            <exclude>*.yml</exclude>
            <exclude>*.properties</exclude>
        </excludes>
    </configuration>
</plugin>

四 參考資料

2)Pippo Deployment: http://www.pippo.ro/doc/deployment.html
 
出自:Pippo Deployment: http://www.pippo.ro/doc/deployment.html
Snapshot Workaround
If you are using a SNAPSHOT version of Pippo as described in the Maven section, a small workaround is necessary due to a Maven bug:
1) Add <outputFileNameMapping>${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}</outputFileNameMapping> to the dependencySet element inside assembly.xml
2) Add <useUniqueVersions>false</useUniqueVersions> to the maven-jar-plugin’s manifest section inside pom.xml

轉載請註明出處哈,have a good time ~ : - )

相關文章
相關標籤/搜索