記錄解決 Spring Boot 專案繼承依賴導致編譯失敗問題
問題背景,公司一個使用 Spring Boot 的 Mutil 專案,根據 Spring Boot 官方文件配置好 Parent 繼承
spring-boot-starter-parent
POM 的時候可以正常編譯執行,但是正式上線時,需要切換到繼承公司統一的外掛集合 Parent POM (這裡暫稱為:common-plugin,該外掛是為了方便執行一些持續整合編譯自動化外掛,例如maven-deploy-plugin
、maven-docker-plugin
等等外掛),然後問題就出現了,編譯不通過,類似提示找不到類,找不到符號的錯誤訊息。
我們都知道,根據 Spring Boot 官網文件
spring-boot-starter-parent
作為父 POM 如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
.....
<dependencies>
<dependency >
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId >spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
類似這樣子配置,此時執行編譯 mvn clean package
是沒有問題噠!但是,目前的情況是,我們的專案需要繼承自己的 Parent POM(common-plugin
),此時在執行編譯,這種方式就行不通啦!因為一個 POM 檔案中只能繼承一個 Parent。當然肯定有人會說,我們可以把 spring-boot-starter-parent
依賴配置到 common-plugin
裡面就可以啦! 首先這種方式是沒有問題的,但是考慮到 common-plugin
作為一個公用 POM 自定義外掛集合,配置後被公司所有專案組繼承使用,且不說會不會出現各種 jar 版本依賴問題, 就 Spring 相關的 jar 版本依賴問題,就比較難處理啦!不能顧此失彼,這種方式成本太高,風險較大。那麼該怎麼辦呢?其實 Spring Boot 官網文件 中給出瞭解決方案,專案父 POM 配置如下:
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
上邊的配置,同樣可以起到繼承 Parent POM 方式相同的作用,同時解決了多 Parent 不支援的問題。這裡要提一點的是,對於 Muti 專案各個子模組中不需要再次配置依賴 spring-boot-dependencies
,只需要繼承父專案 POM 即可(這裡因為父 POM 已經繼承了該依賴,子模組繼承傳遞)。如果在子模組中再次添加了該依賴如下,那麼編譯會報錯。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
</dependency>
所以,子模組不需要再次新增該依賴。還要指出的是,父專案繼承了 spring-boot-dependencies
依賴後,子模組繼承父專案 POM,那麼子模組使用到相關依賴的時候,不需要指定 version 版本了。例如子模組配置如下:
<dependencies>
......
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
不需要指定依賴 version,這是因為在 spring-boot-dependencies
POM 中已經定義好了對應的版本,我們可以從 Maven 倉庫 pom 檔案可以看到這些依賴的版本定義如下:
<dependencies>
......
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
好了,到這裡基本就能解決由於 Parent POM 依賴導致的編譯失敗問題,不過,事情並沒有結束。在修改完以上配置後,再次編譯依舊卡在一個子專案的某個檔案上,提示找不到類,找不到符號,錯誤資訊類似如下:
.....
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project bop-service-crm: Compilation failure: Compilation failure:
[ERROR] /Users/wanyang3/git/mutiproject/sub_project_service/src/main/java/com/test/service/modul/impl/ModuleServiceImpl.java:[21,47] 找不到符號
[ERROR] 符號: 類 RestController
[ERROR] 位置: 程式包 org.springframework.web.bind.annotation
[ERROR] /Users/wanyang3/git/mutiproject/sub_project_service/src/main/java/com/test/service/modul/impl/ModuleServiceImpl.java:[47,2] 找不到符號
[ERROR] 符號: 類 RestController
......
What? 從 IDE 中可以看到這個 org.springframework.web.bind.annotation.RestController
是有的呀!嘗試註釋一下父 POM 中 Parent POM common-plugin
程式碼依賴,發現執行編譯可以通過,說明問題還是出在 common-plugin
中。
仔細觀察下,org.springframework.web.bind.annotation.RestController
這個類屬於 spring-web 包裡面的,然後分別查詢一下這兩個 Parent POM 中 spring 相關的版本,驚奇的發現,他們都定義了各自的 spring 版本。common-plugin
中定義了 <spring.version>3.0.6.RELEASE</spring.version>
,而 spring-boot-dependencies
Parent POM 中定義了 <spring.version>4.3.14.RELEASE</spring.version>
,而只使用後者時,編譯是可以通過的,只使用前者時,編譯不通過。那麼接下來,我們統一一下 Spring 版本為 4.3.14.RELEASE
,看下是否可以編譯通過。
我們採用覆蓋 common-plugin
中的 spring.version 方式來完成版本統一,首先放開上邊註釋的程式碼,然後在專案父 POM 中配置 spring.version 屬性。
<parent>
<artifactId>common-plugin</artifactId>
<groupId>com.plugin.common</groupId>
<version>1.1.0</version>
</parent>
......
<properties>
......
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring.version>4.3.14.RELEASE</spring.version>
</properties>
這樣就可以指定專案使用 4.3.14.RELEASE 版本啦!再次執行編譯,發現編譯通過啦!!!看來還是版本依賴衝突的問題啊!
參考資料