记录解决 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.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.0.2.RELEASEversion>
parent>

.....

<dependencies>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    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>
            
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-dependenciesartifactId>
            <version>2.0.2.RELEASEversion>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

上边的配置,同样可以起到继承 Parent POM 方式相同的作用,同时解决了多 Parent 不支持的问题。这里要提一点的是,对于 Muti 项目各个子模块中不需要再次配置依赖 spring-boot-dependencies,只需要继承父项目 POM 即可(这里因为父 POM 已经继承了该依赖,子模块继承传递)。如果在子模块中再次添加了该依赖如下,那么编译会报错。

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-dependenciesartifactId>
dependency>

所以,子模块不需要再次添加该依赖。还要指出的是,父项目继承了 spring-boot-dependencies 依赖后,子模块继承父项目 POM,那么子模块使用到相关依赖的时候,不需要指定 version 版本了。例如子模块配置如下:

<dependencies>
    ......
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
dependencies>

不需要指定依赖 version,这是因为在 spring-boot-dependencies POM 中已经定义好了对应的版本,我们可以从 Maven 仓库 pom 文件可以看到这些依赖的版本定义如下:

<dependencies>
    ......
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
        <version>2.0.2.RELEASEversion>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <version>2.0.2.RELEASEversion>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
        <version>2.0.2.RELEASEversion>
    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 中定义了 3.0.6.RELEASE,而 spring-boot-dependencies Parent POM 中定义了 4.3.14.RELEASE,而只使用后者时,编译是可以通过的,只使用前者时,编译不通过。那么接下来,我们统一一下 Spring 版本为 4.3.14.RELEASE,看下是否可以编译通过。

我们采用覆盖 common-plugin 中的 spring.version 方式来完成版本统一,首先放开上边注释的代码,然后在项目父 POM 中配置 spring.version 属性。

<parent>
    <artifactId>common-pluginartifactId>
    <groupId>com.plugin.commongroupId>
    <version>1.1.0version>
parent>
......    
<properties>
    ......
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <java.version>1.8java.version>
    <spring.version>4.3.14.RELEASEspring.version>
properties>

这样就可以指定项目使用 4.3.14.RELEASE 版本啦!再次执行编译,发现编译通过啦!!!看来还是版本依赖冲突的问题啊!

参考资料

  • Spring Boot Docs
  • Maven Repo spring-boot-dependencies.pom

你可能感兴趣的:(持续集成,JAVA相关)