1. 程式人生 > >記錄解決 Spring Boot 專案繼承依賴導致編譯失敗問題

記錄解決 Spring Boot 專案繼承依賴導致編譯失敗問題

問題背景,公司一個使用 Spring Boot 的 Mutil 專案,根據 Spring Boot 官方文件配置好 Parent 繼承 spring-boot-starter-parent POM 的時候可以正常編譯執行,但是正式上線時,需要切換到繼承公司統一的外掛集合 Parent POM (這裡暫稱為:common-plugin,該外掛是為了方便執行一些持續整合編譯自動化外掛,例如 maven-deploy-pluginmaven-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 版本啦!再次執行編譯,發現編譯通過啦!!!看來還是版本依賴衝突的問題啊!

參考資料