一次多模块 Spring Boot 项目 mvn package 失败的解决经验

原创
2017/01/16 03:26
阅读数 3W

第一次使用多模块来组织项目代码,算是练手,却一波三折。

使用多模块的项目并不难,只需要在项目的 pom.xml 里声明其他模块(module)的路径,并依赖其它项目的 Maven 包即可。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>

    <artifactId>B</artifactId>
    <groupId>demo</groupId>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>../A</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>demo</groupId>
            <artifactId>A</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

我们将 B 项目这种处在依赖链末端的项目称为终端项目。我们在终端项目里可以惯常地执行 mvn compilemvn package 毫无问题。

如果我们没有指定相应的模块路径,就需要在 A 项目里执行 mvn install 将其发布到本地仓库,否则 B 项目将会因为无法找到 A 包而报错,无法编译。

显然 mvn install 是一种很“重”的操作,它不够干净,而给每个依赖了其他模块的项目添加模块路径重复了很多不必要的代码。此时我们寄希望于使用父子项目的模式,按照官方文档 Guide to Working with Multiple Modules 搭建出被称为 Reactor(反应堆)的项目结构:

./path/to/parent/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>

    <groupId>demo</groupId>
    <artifactId>parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>./path/to/A</module>
        <module>./path/to/B</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>demo</groupId>
                <artifactId>A</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>demo</groupId>
                <artifactId>B</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

./path/to/A/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>parent</artifactId>
        <groupId>demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>A</artifactId>
</project>

./path/to/B/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>parent</artifactId>
        <groupId>demo</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>B</artifactId>

    <dependencies>
        <dependency>
            <groupId>demo</groupId>
            <artifactId>A</artifactId>
        </dependency>
    </dependencies>
</project>

此时项目的构建要稍费心思。

简单的在终端项目里使用 mvn compile 编译是不行的,因为找不到其他模块的 Maven 包。这时我们可以使用 mvn install 的老办法,也可以使用 -am--also-make 参数使 Maven 构建被依赖的包。

在 parent 包下使用 mvn compile -ammvn compile -pl B -am 的作用是一致的(-pl 参数的作用是指定编译模块)。因为 Maven 会分析依赖树并找到可行的顺序来构建。

当我将多模块项目应用于 Spring Boot 框架的时候,使用 IDE 编译、运行都没问题,但执行 mvn package 却发生了问题。明明被依赖包的 class 和 jar 包都输出了,却报错找不到被依赖的包。

百思不得其解,竟然发现 mvn compilemvn package 可行!令人迷醉。细心查看 Maven 构建过程,发现被依赖的包执行了 spring-boot-maven-plugin:repackage 目标,这嫌疑很大。

最终在 StackOverflow 找到了解决方案:

我们不应该给 parent 项目添加 spring-boot-maven-plugin 构建插件,而应该给终端项目使用,因为这个插件的 repackage 目标会处理 jar 包,导致依赖它的模块无法使用它。在 parent 项目中使用它会导致每个子项目都执行了该目标,进而出现编译失败。

仅此记录,希望能够减少大家的 debug 时间,祝大家生活愉快。

展开阅读全文
打赏
9
32 收藏
分享
加载中
解决了,多谢!
2018/11/02 17:55
回复
举报
<configuration>
<classifier>exec</classifier>
</configuration>

增加上面的配置可以打包两个,一个原始包,一个执行包。
https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/repackage-classifier.html
2018/10/15 09:57
回复
举报

引用来自“傅易君”的评论

引用来自“jack_jones”的评论

我也是这么配置的,parent下面有几个module,b-module依赖于a-module,在b-module里执行package,提示找不到a-module的jar文件

@jack_jones 因为 b-module 里没有配置 a-module 的模块路径,这个路径在 parent 里配置了,所以你能从 parent 项目 mvn package -pl 实现你的目标。
用pl就可以了,3q
2017/01/24 10:04
回复
举报

引用来自“jack_jones”的评论

我也是这么配置的,parent下面有几个module,b-module依赖于a-module,在b-module里执行package,提示找不到a-module的jar文件

@jack_jones 因为 b-module 里没有配置 a-module 的模块路径,这个路径在 parent 里配置了,所以你能从 parent 项目 mvn package -pl 实现你的目标。
2017/01/23 18:10
回复
举报
我也是这么配置的,parent下面有几个module,b-module依赖于a-module,在b-module里执行package,提示找不到a-module的jar文件
2017/01/23 11:33
回复
举报
更多评论
打赏
5 评论
32 收藏
9
分享
返回顶部
顶部