maven实战(三)——多模块项目的POM重构
1.重复
举个栗子
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactid>spring-beans</artifactId> 4 <version>2.5</version> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework</groupId> 8 <artifactid>spring-context</artifactId> 9 <version>2.5</version> 10 </dependency> 11 <dependency> 12 <groupId>org.springframework</groupId> 13 <artifactid>spring-core</artifactId> 14 <version>2.5</version> 15 </dependency>
代码有重复,version属性可以抽取出来,日后升级依赖版本的时候,只需要修改一处,而且也能避免漏掉升级某个依赖。
1 <properties> 2 <spring.version>2.5</spring.version> 3 </properties> 4 <depencencies> 5 <dependency> 6 <groupId>org.springframework</groupId> 7 <artifactid>spring-beans</artifactId> 8 <version>${spring.version}</version> 9 </dependency> 10 <dependency> 11 <groupId>org.springframework</groupId> 12 <artifactid>spring-context</artifactId> 13 <version>${spring.version}</version> 14 </dependency> 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactid>spring-core</artifactId> 18 <version>${spring.version}</version> 19 </dependency> 20 </depencencies>
2.消除多模块依赖配置重复
举个栗子
考虑这样一个不大不小的项目,它有10多个Maven模块,这些模块分工明确,各司其职,相互之间耦合度比较小,这样大家就能够专注在自己的模块中进行开发而不用过多考虑他人对自己的影响。
我开始对模块A进行编码了,首先就需要引入一些常见的依赖如JUnit、Log4j等等:
1 <dependency> 2 <groupId>junit</groupId> 3 <artifactid>junit</artifactId> 4 <version>4.8.2</version> 5 <scope>test</scope> 6 </dependency> 7 <dependency> 8 <groupId>log4j</groupId> 9 <artifactid>log4j</artifactId> 10 <version>1.2.16</version> 11 </dependency>
同事在开发模块B,他也要用JUnit和Log4j
1 <dependency> 2 <groupId>junit</groupId> 3 <artifactid>junit</artifactId> 4 <version>3.8.2</version> 5 </dependency> 6 <dependency> 7 <groupId>log4j</groupId> 8 <artifactid>log4j</artifactId> 9 <version>1.2.9</version> 10 </dependency>
问题,同事漏了JUnit依赖的scope,而且版本也不一致
解决方法:使用继承机制以及dependencyManagement元素
dependencyManagement只会影响现有依赖的配置,但不会引入依赖
例如我们可以在父模块中配置如下:
1 <dependencyManagement> 2 <dependencies> 3 <dependency> 4 <groupId>junit</groupId> 5 <artifactid>junit</artifactId> 6 <version>4.8.2</version> 7 <scope>test</scope> 8 </dependency> 9 <dependency> 10 <groupId>log4j</groupId> 11 <artifactid>log4j</artifactId> 12 <version>1.2.16</version> 13 </dependency> 14 </dependencies> 15 </dependencyManagement>
如果某个子模块需要使用JUnit和Log4j的时候,我们就可以简化依赖配置成这样:
1 <dependency> 2 <groupId>junit</groupId> 3 <artifactid>junit</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>log4j</groupId> 7 <artifactid>log4j</artifactId> 8 </dependency>
在多模块Maven项目中,dependencyManagement几乎是必不可少的,因为只有它是才能够有效地帮我们维护依赖一致性。
可以把dependencyManagement放到单独的专门用来管理依赖的POM中,然后在需要使用依赖的模块中通过import scope依赖,就可以引入dependencyManagement。例如可以写这样一个用于依赖管理的POM:
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.sample</groupId> <artifactId>sample-dependency-infrastructure</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactid>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactid>log4j</artifactId> <version>1.2.16</version> </dependency> </dependencies> </dependencyManagement> </project>
然后就可以通过非继承的方式来引入这段依赖管理配置:
1 <dependencyManagement> 2 <dependencies> 3 <dependency> 4 <groupId>com.juvenxu.sample</groupId> 5 <artifactid>sample-dependency-infrastructure</artifactId> 6 <version>1.0-SNAPSHOT</version> 7 <type>pom</type> 8 <scope>import</scope> 9 </dependency> 10 </dependencies> 11 </dependencyManagement> 12 13 <dependency> 14 <groupId>junit</groupId> 15 <artifactid>junit</artifactId> 16 </dependency> 17 <dependency> 18 <groupId>log4j</groupId> 19 <artifactid>log4j</artifactId> 20 </dependency>
这样,父模块的POM就会非常干净,由专门的packaging为pom的POM来管理依赖,也契合的面向对象设计中的单一职责原则。此外,我们还能够创建多个这样的依赖管理POM,以更细化的方式管理依赖。这种做法与面向对象设计中使用组合而非继承也有点相似的味道。
3.消除多模块插件配置重复
使用pluginManagement元素管理插件。一个常见的用法就是我们希望项目所有模块的使用Maven Compiler Plugin的时候,都使用Java 1.5,以及指定Java源文件编码为UTF-8,这时可以在父模块的POM中如下配置pluginManagement:
1 <build> 2 <pluginManagement> 3 <plugins> 4 <plugin> 5 <groupId>org.apache.maven.plugins</groupId> 6 <artifactId>maven-compiler-plugin</artifactId> 7 <version>2.3.2</version> 8 <configuration> 9 <source>1.5</source> 10 <target>1.5</target> 11 <encoding>UTF-8</encoding> 12 </configuration> 13 </plugin> 14 </plugins> 15 </pluginManagement> 16 </build>
这段配置会被应用到所有子模块的maven-compiler-plugin中,由于Maven内置了maven-compiler-plugin与生命周期的绑定,因此子模块就不再需要任何maven-compiler-plugin的配置了。
与依赖配置不同的是,通常所有项目对于任意一个依赖的配置都应该是统一的,但插件却不是这样,例如你可以希望模块A运行所有单元测试,模块B要跳过一些测试,这时就需要配置maven-surefire-plugin来实现,那样两个模块的插件配置就不一致了。这也就是说,简单的把插件配置提取到父POM的pluginManagement中往往不适合所有情况,那我们在使用的时候就需要注意了,只有那些普适的插件配置才应该使用pluginManagement提取到父POM中。