在实际使用maven构建项目的时候,可能会遇到下面的问题:
一个项目包含了很多的模块,这些模块每一个都是一个独立的mvn项目,那么一次构建必须分别为每一个模块运行mvn的命令,有没有办法只运行一个命令就把这些模块全部打包?
有,使用聚合maven。
我们需要新建一个聚合的maven项目,这个项目本身没有任何业务的逻辑,只是单纯负责打包其他模块的。下面是一个例子:
假设项目里现在有data模块和service模块。那么现在新建一个聚合的模块:app-aggregator
aggregator的pom与其他只有两个区别,一是package类型为pom,而是用modules标签包含了要打包的模块:
<?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>
<groupId>com.liyao</groupId>
<artifactId>app-aggregator</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>data</module>
<module>service</module>
</modules>
</project>
这里的data和service模块就是普通的maven项目,就不贴了,目录在aggregator下。
这样我们只需要运行aggregator的mvn命令就可以打包里面的模块了。
运行:mvn clean install
可以看到,总共打包了三个jar文件,包括了aggregator项目。
在其他项目中想要引入子模块的jar包,还必须使用子模块的坐标,使用aggregator无法引入子模块的依赖。可以看到,这个aggragator的作用仅仅是为了打包子模块。
使用聚合可以新建一个项目来统一打包子模块,解决了开始提到的问题。
但是实际中还会有另一个问题,子模块之间往往会有很多共性的配置,比如spring相关的jar等,这些相同的配置如果要在每一个子模块里都配置一次,无疑很麻烦,而且新加入的模块也不利于扩展,有很多冗余配置。
这个问题可以通过继承解决。当然我们还是需要新建一个mvn项目作为父项目。
新建ap-parent项目,位于aggregator目录下,与子模块平级。
<?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>
<groupId>com.liyao</groupId>
<artifactId>ap-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
</project>
其pom除了必须制定package为pom以外,没有不同。
现在的目录:
app-aggregator
|-ap-parent
|-data
|-service
在aggregator里添加ap-parent项目。修改data和service子模块的pom:
<?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">
<parent>
<groupId>com.liyao</groupId>
<artifactId>ap-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../ap-parent/pom.xml</relativePath>
</parent>
<artifactId>data</artifactId>
<version>1.0</version>
<modelVersion>4.0.0</modelVersion>
</project>
service类似。
这里使用parent标签指定了父pom的坐标。其中的relativePath制定了父pom的物理位置,默认是../pom.xml。但是这里因为是平级的,所以需要重新定义。然后再指定子模块的名字和版本。
这样子模块就可以继承父模块的配置了。
可以继承的配置有:
那么这里重点说下dependecyManagement相关的。以下简称dp。
我们当然会想把公共的依赖放入父pom中,这样我们可以使用dependences标签把公共依赖加入到父pom中。但是有一个问题,万一有一个子模块继承了父pom,但是想要继承里面一部分配置而不是全部呢?
这就需要使用dp标签了。dp与dependences的区别在于,父pom中的dependences定义的依赖,子模块是一定会引入的。但是dp的依赖则不会,除非在子模块中使用dependences定义相同的依赖。所以dp引不引入,并不是父pom说了算,还是需要在子模块定义dependences。这不是多此一举吗?所以这里的dp仅仅是提供配置的公共模板,比如版本,这样有利于管理子模块引入的公共依赖的版本。但是最后引不引入还是看子模块。如果子模块没有定义dependences,那么就不引入。如果定义了dependences没有定义version就是用父pom的dp里定义的version,如果指定了version,就是用子模块的version。
看例子:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8</version>
</dependency>
</dependencies>
在父pom里加入了上述依赖,子模块没有定义,但是子模块中可以使用junit,说明确实是直接继承了父pom的dependences。
import org.junit.Before;
public class Dao {
@Before
public void read(){
System.out.println("read");
}
}
接着把pom中修改一下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8</version>
</dependency>
</dependencies>
</dependencyManagement>
子模块不变,之前的junit就无法使用了:
然后再在子模块中加入dependences,且不指定version
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
junit又可以使用了。且版本与父pom一致。
然后再指定一下版本:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
可以看到子模块的junit就是新指定的4.12了。
所以,dp与dependences的区别就搞清楚了。dp只提供默认版本,不会引入。类似的还有pluginManagement。
实际中,通常会把聚合与继承的项目搞成一个,作为一个目录。
还有一个问题,对于聚合pom,有一个反应堆的概念。就是modules里的模块,如果有依赖关系,那么构建时必须按照依赖的顺序来构建,如果没有依赖则直接构建。maven会把所有的模块放入一个反应堆中,然后分析堆里的模块的依赖关系。所以最终的构建顺序是:按序扫描,如果没有依赖就直接构建,否则就递归先构建依赖。