Author: Jia, Weigang.
Maven中的POM大致有两个目的:
- 开发人员用POM编译和发布构件
- 被其他项目用于依赖管理
对于第一个目的,POM中的关于编译和发布的元素都是有意义的。对于第二个目的,POM中的关于编译和发布的元素就没有意义了。比如,一个项目A依赖于构件B,项目A仅仅需要知道构件B的GroupId、ArtifactId、Version以及构件B的依赖,其他的元素意义都不是很大。
Maven的发布方式是把编译时的POM直接拷贝到远程仓库。这样对于构件的用户而言,他们将看到编译时期POM的各种元素。如果POM有多层父POM,用户也会看到。父POM中可能定义了许多<dependencyManagement>,如果用户需要确定一个依赖的版本,可能需要一层一层的向上层父POM查找,比较繁琐。
对于开发人员编译和发布时使用的POM应该和构件使用者的POM有所不同,对于后者,他们只需要关心构件的GroupId、ArtifactId、Version以及构件的依赖。扁平化POM可以解决这个问题,在扁平化POM中只有构件的GroupId、ArtifactId、Version以及构件的依赖,没有Parent元素。对于每个构件的依赖,它的Version都是被解析过的。
下面详细讨论扁平POM中的元素:
元素 | 在扁平化POM中变换 | 注释 |
modelVersion | “4.0.0” | 固定值 |
groupId artifactId version packaging | 被解析过 | 可能从父POM中解析version |
dependencies | 被解析过 | 可能从父POM的<dependencyManagement>中解析出dependencies的所有属性(主要是version)。Scope是test的dependency将会被移除 |
Other Elements | 被移除 | 构件的用户不关心这些元素 |
我扩展了maven-deploy-plugin ,增加了一个enableFlatPom参数来实现了扁平化POM,如果这个参数的值是true,maven-deploy-plugin会生成扁平化POM并把它设置成当前项目的POM,然后发布到远程仓库。
下面是一个扁平化POM的例子:
原始的父POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.ebay.test</groupId>
<artifactId>webtestjavaParent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<webtestjava.version>1.0.0-SNAPSHOT<webtestjava.version>
</properties>
<modules>
<module>webtest-java</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.9-SNAPSHOT</version>
<configuration>
<enableFlatPom>true</enableFlatPom>
</configuration>
</plugin>
</plugins>
</build>
</project>
原始的子POM
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ebay.test</groupId>
<artifactId>webtestjavaParent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>webtestjava</artifactId>
<version>${webtestjava.version}</version>
<packaging>jar</packaging>
<dependencies>
<!-- Internal dependencies with project version -->
<dependency>
<groupId>com.ebay.test</groupId>
<artifactId>webtestjava-module2</artifactId>
<version>${webtestjava.version}</version>
</dependency>
<!-- External dependencies with managed version -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
扁平化POM
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.ebay.test</groupId>
<artifactId>webtestjava</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.ebay.test</groupId>
<artifactId>webtestjava-module2</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
</project>
扁平化POM实现:
修改maven-deploy-plugin中的类org.apache.maven.plugin.deploy. DeployMojo。
1. 增加enableFlatPom参数:
@Parameter( property = "enableFlatPom", defaultValue = "false" )
private boolean enableFlatPom;
2.当enableFlatPom参数设置为true的时候,生成扁平化的POM并设置为当前项目的POM。
if ( enableFlatPom )
{
useFlatPom( request.getProject() );
artifact = request.getProject().getArtifact();
pomFile = request.getProject().getFile();
}
private void useFlatPom( MavenProject project )
throws MojoExecutionException
{
generateFlatPom( project );
applyFlatPom( project );
}
3.useFlatPom方法会生成扁平化的POM并设置为当前项目的POM,createFlatPomFile方法生成一个空的POM文件,createFlatModel方法生成扁平化POM的模型,然后把扁平化POM的模型写入到POM文件。
private void generateFlatPom( MavenProject project )
throws MojoExecutionException
{
getLog().info( "Generating flat pom for project " + project.getName() );
File flatPomFile = createFlatPomFile( project );
Model flatPomModel = createFlatModel( project );
MavenXpp3Writer pomWriter = new MavenXpp3Writer();
Writer fileWriter = null;
try
{
fileWriter = WriterFactory.newXmlWriter( flatPomFile );
pomWriter.write( fileWriter, flatPomModel );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Cannot write to flat pom file for project " + project.getName(), e );
}
finally
{
IOUtil.close( fileWriter );
}
}
4. applyFlatPom方法将扁平化POM设置为当前项目的POM并更新相应元数据。
private void applyFlatPom( MavenProject project )
throws MojoExecutionException
{
getLog().info( "Applying flat pom for project " + project.getName() );
File flatPomFile = new File( getFlatPomDir( project.getBasedir() ), "pom.xml" );
if ( !flatPomFile.exists() )
{
throw new MojoExecutionException( "Cannot find flat pom file in path " + flatPomFile.getAbsolutePath()+ " for project " + project.getName() );
}
project.setFile( flatPomFile );
Artifact artifact = ArtifactUtils.copyArtifact( project.getArtifact() );
Collection metadataList = project.getArtifact().getMetadataList();
for ( ArtifactMetadata metadata : (Collection<ArtifactMetadata>) metadataList )
{
if ( metadata instanceof RepositoryMetadata )
{
artifact.addMetadata( metadata );
}
}
artifact.addMetadata( new ProjectArtifactMetadata( artifact, flatPomFile ) );
project.setArtifact( artifact );
}
扁平化POM是原始POM的精简版,它的目标用户是构件的使用者,所以对于项目的开发和编译发布的元素都会被移除。将扁平POM发布到远程仓库中将会对构件用户更加友好。