環境
- SpringCloud Greenwich.SR2
- Maven3.0.5
- Idea2019.3
項目結構
Service1 依賴 Common
Service2 依賴 Common
Common 無任何依賴
遇到了什么問題?
mvn package 父項目或 Service 都會報錯,也就是說在 maven 多模塊結構的項目中,引用了一個沒有程序入口的項目將會打包失敗
問題分析 + 解決
下面我對兩種無法通過打包的情況進行分析,並給出解決方案。
Service 項目無法打包
[ERROR] Failed to execute goal on project Service1: Could not resolve dependencies for project org.ccccye:Service:jar:0.0.1-SNAPSHOT: Could not find artifact org.ccccye:common:jar:0.0.1-SNAPSHOT -> [Help 1]
從異常信息中可以知道兩件事
- Maven 執行 goal 失敗
- goal 執行失敗是因為 Service1 項目所依賴的 common.jar 包找不到
那么問題根源應該是 Service1 項目找不到 common.jar 包造成的。通過翻閱資料和實驗,發現當項目單獨打包時,不會主動去編譯所依賴的模塊項目,而是去本地倉庫找 jar 包,如果用 maven install 把 common.jar 安裝到本地倉庫,是不是能解決問題呢?答案是可以的,在 common 項目執行 mvn install 就可以將 common.jar 安裝到本地倉庫了,,,等等,這里還會出現另外一個問題,接下來繼續講。
父項目無法打包
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.1.7.RELEASE:repackage (repackage) on project weather-common: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.1.7.RELEASE:repackage failed: Unable to find main class -> [Help 1]
從異常信息中可以知道兩件事
- 同樣是 Maven 的 goal 執行失敗了,並且明確是 repackge 功能,這說明在將普通 jar 重新打包成可執行 jar 的過程中發生了錯誤
- 異常信息中還說了報錯原因是找不到 main 函數造成的
因此問題在 main 函數報錯上,common 項目根本沒有 main 函數並且不需要,這是難為我胖虎啊??翻了資料發現,maven 生成 jar 包的順序是 先生成普通 jar 包,再包裝成可執行 jar(普通 jar 會被覆蓋),注意:可執行程序肯定要有 main 函數的,那么我們可以通過設置 spring-boot-maven-plugin 插件的 layout 參數,賦值為 NONE,maven 只打包所需的依賴,不掃描 main 函數
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>NONE</layout> <!--讓maven不打包可執行jar,不掃描項目的main函數-->
<classifier>exec</classifier> <!--普通jar和可執行jar不同名,普通jar為xx.jar , 可執行jar為 xx-exec.jar-->
</configuration>
</plugin>
如上,配置了 layout=NONE 解決了 main 函數問題,在實驗過程中發現,Service1 能正常編譯,一運行就報異常,提示 找不到或無法加載主類 ,原來 Service 想要的 jar 是一個純粹的普通 jar 包,雖然設置了 layout=NONE,但 maven 還是會再打包一次,並且會覆蓋普通的 jar,讓 Service 雖然加載了 jar 包卻找不到 jar 包里面所包含的類,在這里我的解決方案是配置普通 jar 跟二次打包的 jar 不同名,也就是配置
<classifier>exec</classifier>
到此,問題的原因和解決方案都有了,這里總結出來的知識點有
- 子項目 package 時的依賴是去本地倉庫或者遠程倉庫查找的,不會主動編譯本地項目並依賴
- Maven 默認會執行 repackge 這個 goal
- repackge 將普通 jar 改成 xx.jar.original,可執行 jar 為 xx.jar,可以通過 classifier 參數來修改可執行 jar 的名字后綴
- 可執行 jar 默認尋找一個 main 函數,通過配置 layout=none 取消
第二種解決方案
還有一種解決方案,在主項目 pom 更換插件,將 spring-boot-maven-plugin 改為 maven-compiler-plugin,具體的配置為
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
然后在主項目運行 package 命令就可以整體編譯打包了。不過要打包 Service 子項目的話,Common 項目得手動 install 安裝到本地倉庫才行。
關聯的知識
- Maven 的插件模型
總結
主 pom | 打包方式 | service pom | common pom |
---|---|---|---|
spring-boot-maven-plugin | 整體打包 | spring-boot-maven-plugin | spring-boot-maven-plugin 增加配置:1. layout=none 2.classifier=exec |
部分打包 | spring-boot-maven-plugin | spring-boot-maven-plugin 增加配置:1. layout=none 2.classifier=exec。打包前需要 install | |
maven-compiler-plugin | 整體打包 | spring-boot-maven-plugin | 無 build 節點 |
部分打包 | spring-boot-maven-plugin | 無 build 節點,打包前需要 install |
可以看到部分打包都需要提前 install 公共項目,否則 package 過程中找不到 jar 包;使用 maven-compiler-plugin 不需要增加配置。
值得一說的是這兩個插件並不是同一個類型的,只是剛好能解決問題,它們的區別如下
- spring-boot-maven-plugin 是打包插件
- maven-compiler-plugin 是編譯插件
這也就不難理解,為什么 spring-boot-maven-plugin 需要增加配置了,因為它不負責編譯,只能對編譯出來的東西進行打包,因此不能對其他項目進行編譯然后打包到 jar 里;maven-compiler-plugin 可以進行編譯,在編譯階段能將所依賴的其他項目按順序編譯,最后打包進 jar