我们之前创建的这个maven-test项目,只包含一个模块。但是在实际开发中,由于业务庞大,经常会把一个大项目分为多个模块。或者是把controller、service、dao等业务层都作为一个模块。最上层是一个总的模块,里面包含很多子模块,同时,最上层的模块还作为一个父模块,和子模块形成依赖关系。整个大项目形成一个父子工程的结构。
先回顾下我们maven-test的结构:
maven-test就是一个maven的模块,里面有src目录,可以在里面编写业务代码。
下面我们改造一下这个项目,在里面创建几个子模块。
聚合
先删掉maven-test目录下的src目录,然后在maven-test上右键选择新建module:
新建三个module:maven-test-controller、maven-test-service、maven-test-dao:
在maven-test的pom中可以看到多了下面的内容:
<packaging>pom</packaging>
<modules>
<module>maven-test-controller</module>
<module>maven-test-service</module>
<module>maven-test-dao</module>
</modules>
maven-test就是聚合项目,里面的maven-test-controller、maven-test-service、maven-test-dao是三个子模块。而maven-test作为聚合项目,一般会在目录结构的最顶层,并且聚合项目的packaging必须要为pom。
经过maven的聚合,我们就可以通过命令来构建所有的这些模块,也可以只选择一部分模块。
当选择构建聚合模块时,maven就会解析聚合模块的pom文件,分析要构建的模块,并且计算出一个反应堆构建顺序,根据这个顺序依次构建各个模块。
可以在命令行输入 mvn clean install,看下打印出来的日志,我这里截取一部分日志贴出来:
mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] maven-test [pom]
[INFO] maven-test-controller [jar]
[INFO] maven-test-service [jar]
[INFO] maven-test-dao [jar]
.....省略.......
[INFO] Reactor Summary:
[INFO]
[INFO] maven-test 1.0-SNAPSHOT ............................ SUCCESS [ 0.801 s]
[INFO] maven-test-controller .............................. SUCCESS [ 0.898 s]
[INFO] maven-test-service ................................. SUCCESS [ 0.067 s]
[INFO] maven-test-dao 1.0-SNAPSHOT ........................ SUCCESS [ 0.063 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.961 s
[INFO] Finished at: 2018-10-12T08:17:37+08:00
[INFO] ------------------------------------------------------------------------
从日志中可以看到,maven先计算了构建顺序,并且所有的模块都被构建了。
通过maven的聚合,我们就可以很灵活的构建项目,一个命令就可以把所有的项目构建了,而且还可以只构建项目中的某几个模块,加快构建速度。
继承
有了聚合,我们就可以在一个聚合项目中包含多个子模块,那就会有个问题,不同子模块中都会引入共同的依赖,造成太多的冗余,这就需要用到maven的继承了,在聚合模块中,一般都会有继承。我们创建maven-test-controller、maven-test-service、maven-test-dao这三个模块中,在pom文件中就可以看到下面的声明:
<parent>
<artifactId>maven-test</artifactId>
<groupId>com.baidu.test</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>maven-test-controller</artifactId>
这就是maven-test-controller的pom文件,里面有个parent标签,标识maven-test-controller的parent是maven-test模块。在maven-test-controller中也就只需要声明artifactId的值即可,groupId和version默认继承父模块的。当然,子模块也可以显式的声明groupId和version,但是一般的约定,都是要和父模块保持一致。
通过maven继承,子模块就可以继承父模块声明的依赖,我们的代码中就不会有那么多的冗余了。除非子模块需要声明新的依赖或者是子模块和父模块的依赖版本不一样,才需要在子模块中进行声明。
这种方式虽然可以在子模块中少很多声明,其实并不推荐,因为子模块默认是继承父模块中所有的声明的,如果一个子模块本来不需要这些依赖,那也会默认继承。可以在父模块中通过<dependencyManagement>
标签来让子模块既能继承到父模块的依赖配置,又能保持使用的灵活性。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
通过dependencyManagement
元素声明的依赖,不会实际引入子模块中,但是又能够约束dependencies
下的依赖使用。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
父模块通过dependencyManagement
元素声明的依赖后,子模块如果需要引入的依赖,就可以通过<dependency>
显式引入依赖,但是不需要声明version,因为父项目中已经定义了version。如果子模块不声明依赖的话,即使父模块在dependencyManagement
声明了,子模块也不会引入。
和dependencyManagement
对应,pluginManagement
是对插件进行同样方式的声明的,该元素中配置的依赖不会造成实际的插件调用行为:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.3</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
通过这种方式,就可以在父模块中统一项目范围中依赖的版本,同时不会造成子模块多余的引用,减少多个子模块之间独自声明出现的使用版本不一致的情况,降低依赖冲突的几率。