依赖管理
上一节我们说到可以继承dependencies元素,我们很容易想到把这一特性应用到accout-parent中。子模块account-email和account-persist同时依赖了org.springframework:spring-core:2.5.6,spring-beans:2.5.6,spring-context:2.5.6,junit:junit:4.7。以此可以将这些公共依赖放到父模块account-parent中,两个子模块就能移除这些依赖,简化配置。
上述方法时可行的,但是有问题。因为我们无法确定将来添加子模块就一定需要这四个模块依赖。Maven提供dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下依赖声明不会引入实际的依赖,不过他能够约束dependencies下依赖的使用。例如,可以在account-parent下加入这样的dependencyManagement配置,代码如下:
<modelVersion>4.0.0</modelVersion>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Account Parent</name>
<properties>
<springframework.version>2.5.6</springframework.version>
<junit.version>4.7</junit.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
现在修改account-email配置如下
<properties>
<javax.mail.version>1.4.1</javax.mail.version>
<greenmail.version>1.3.1b</greenmail>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springframework.version}</version>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>${junit.mail.version}</version>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>${greenmail.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
使用这种依赖管理机制似乎不能减少太多了POM配置,但是我还是强烈推荐采用这种方法。原因是在于父pom中使用dependencyManagement声明依赖能够统一项目范围中依赖的版本,降低依赖的冲突。
如果子模块中不声明依赖的使用,即使该依赖已经在父pom的dependencyManagement中声明了也不会产生任何实际的效果,如果account-persist:
<properties>
<dom4j.version>1.6.1</dom4j.version>
</properties>
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>${dom4j.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
这里没有声明spring-context-support,那么该依赖就不会被引入。
这里顺便提一下import的依赖范围,这个依赖范围只在dependencyManagement元素下才有效果。使用该范围的依赖通常指向一个POM,作用是将目标POM中的dependencyManagement配置导入并合并到当前pom的dependencyManagement元素中。例如:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.juvenxu.mvnbook.account</groupId>
<artifactId>account-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
插件管理
Maven提供了dependencyManagement元素帮助管理依赖,类似的Maven也提供了pluginManagement元素帮助管理插件。在该元素中配置的依赖不会造成实际的插件调用行为,当POM中配置了真正了plugin元素,并且其groupId和artifactId与pluginManagement中配置的插件匹配时,pluginManagement的配置才会影响到实际的插件行为
例如:
<bulid>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
<execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</bulid>
将其jar-no-fork目标绑定到了verity生命周期阶段,以生成项目源码包,当子模块需要生成源码包的时候,只需要如下简单的配置,代码如下:
<bulid>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<plugin>
</plugins>
</build>
如果子模块需要不同于父模块的插件配置,可以自行配置以覆盖父模块的pluginManagement配置。有了pluginManagement元素,account-email和account-persist的pom也得以简化。他们都配置了maven-compiler-plugin和maven-resoureces-plugin。可以将这两个插件配置移到account-parent的pluginManagement元素中。
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugins>
</pluginManagement>
</build>
account-email和accoun-persist可以完全移除关于maven-compiler-plugin和maven-resources-plugin的配置,但他们仍能享受这两个插件服务。
当项目的多个模块有同样的插件配置时,应当将配置移到父pom的pluginManagement的元素中。即使各个模块对同一个插件的具体配置不尽相同,也应当使用父pom的pluginManagement元素统一声明插件的版本。甚至可以要求将所有用到的插件的版本在父pom的pluginManagement元素中声明,子模块使用插件时不配置版本信息,这么做可以统一项目的插件版本,避免潜在的插件不一致或者不稳定问题,也更易于维护。