阅读更多
1 编写Maven插件的一般步骤
编写Maven插件的主要步骤如下:
创建一个maven-plugin项目 :
插件本身也是Maven项目,特殊的地方在于它的packaging必须是maven-plugin
用户可以使用maven-archetype-plugin
快速创建一个Maven插件项目
为插件编写目标 :
每个插件都必须包含一个或多个目标,Maven称之为Mojo(与Pojo对应,后者指Plain Old Java Object,这里指Maven Old Java Object)
编写插件的时候必须提供一个或者多个继承自AbstractMojo的类
为目标提供配置点 :
大部分Maven插件其目标都是可配置的,因此在编写Mojo的时候需要注意提供可配置的参数
编写代码实现目标行为 :
错误处理及日志 :
当Mojo发生异常时,根据情况控制Maven的运行状态。在代码中编写必要的日志以便为用户提供足够的信息
测试插件 :
编写自动化的测试代码测试行为,然后再实际运行插件以验证其行为
2 案例:编写一个用于代码行统计的Maven插件
2.1 创建一个Maven插件项目
使用maven-archetype-plugin
快速创建一个Maven插件项目
看到以下输出
1 2 3 4 ... 1078: remote -> org.apache.maven.archetypes:maven-archetype-plugin (An archetype which contains a sample Maven plugin.) ... Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1082: <在这里键入>
然后键入1078,即Maven插件项目(编号可能有变动,参考实际输出信息)
1 Choose a number: 3: <在这里键入>
键入Enter
1 Define value for property 'groupId': : <在这里键入>
键入groupId,我输入的是org.liuyehcf
1 Define value for property 'artifactId': : <在这里键入>
键入artifactId,我输入的是maven-count-plugin
1 Define value for property 'version': 1.0-SNAPSHOT: : <在这里键入>
键入Enter
1 Define value for property 'package': org.liuyehcf: : <在这里键入>
键入Enter
键入Enter
生成了如下的Pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 <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 > org.liuyehcf</groupId > <artifactId > maven-count-plugin</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > maven-plugin</packaging > <name > maven-count-plugin Maven Plugin</name > <url > http://maven.apache.org</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.apache.maven</groupId > <artifactId > maven-plugin-api</artifactId > <version > 2.0</version > </dependency > <dependency > <groupId > org.apache.maven.plugin-tools</groupId > <artifactId > maven-plugin-annotations</artifactId > <version > 3.2</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.codehaus.plexus</groupId > <artifactId > plexus-utils</artifactId > <version > 3.0.8</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.8.2</version > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-plugin-plugin</artifactId > <version > 3.2</version > <configuration > <goalPrefix > maven-count-plugin</goalPrefix > <skipErrorNoDescriptorsFound > true</skipErrorNoDescriptorsFound > </configuration > <executions > <execution > <id > mojo-descriptor</id > <goals > <goal > descriptor</goal > </goals > </execution > <execution > <id > help-goal</id > <goals > <goal > helpmojo</goal > </goals > </execution > </executions > </plugin > </plugins > </build > <profiles > <profile > <id > run-its</id > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-invoker-plugin</artifactId > <version > 1.7</version > <configuration > <debug > true</debug > <cloneProjectsTo > ${project.build.directory}/it</cloneProjectsTo > <pomIncludes > <pomInclude > */pom.xml</pomInclude > </pomIncludes > <postBuildHookScript > verify</postBuildHookScript > <localRepositoryPath > ${project.build.directory}/local-repo</localRepositoryPath > <settingsFile > src/it/settings.xml</settingsFile > <goals > <goal > clean</goal > <goal > test-compile</goal > </goals > </configuration > <executions > <execution > <id > integration-test</id > <goals > <goal > install</goal > <goal > integration-test</goal > <goal > verify</goal > </goals > </execution > </executions > </plugin > </plugins > </build > </profile > </profiles > </project >
Maven插件项目的POM有两个特殊的地方
它的packaging必须为maven-plugin,这种特殊的打包类型能控制Maven为其在生命周期阶段绑定插件处理相关的目标,例如在compile阶段,Maven需要为插件项目构建一个特殊插件描述符文件
包含了一个artifactId为maven-plugin-api的依赖,该依赖中包含了插件开发所必须的类,例如AbstractMojo
2.2 为插件编写目标
在上一步骤中,使用Archetype生成的插件项目包含了一个名为MyMojo的Java文件,将其删除,创建一个CountMojo,其代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 package org.liuyehcf;import org.apache.maven.plugin.AbstractMojo;import org.apache.maven.plugin.MojoExecutionException;import org.apache.maven.plugin.MojoFailureException;import org.apache.maven.plugins.annotations.LifecyclePhase;import org.apache.maven.plugins.annotations.Mojo;import org.apache.maven.plugins.annotations.Parameter;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;import java.util.List;@Mojo(name = "count", defaultPhase = LifecyclePhase.PROCESS_SOURCES) public class CountMojo extends AbstractMojo { private static final String[] INCLUDES_DEFAULT = {".java" , ".xml" , ".properties" }; @Parameter(defaultValue = "${basedir}", property = "baseDir", required = true) private File baseDir; private List<File> legalFiles; @Parameter(property = "suffixes") private String[] suffixes; public void execute () throws MojoExecutionException, MojoFailureException { initialize(); if (!isParamLegal()) { getLog().error("Param is illegal" ); return ; } findOutAllLegalFilesRecursionWithDirection(baseDir); countEachLegalFile(); } private void initialize () { legalFiles = new ArrayList <File>(); if (suffixes == null || suffixes.length == 0 ) { suffixes = INCLUDES_DEFAULT; } getLog().info("baseDir: " + baseDir); } private boolean isParamLegal () { if (baseDir == null || !baseDir.isDirectory()) { return false ; } return true ; } private void findOutAllLegalFilesRecursionWithDirection (File baseDir) { File[] curFiles = baseDir.listFiles(); for (File file : curFiles) { if (file.isFile() && isLegalFileName(file.getName())) { legalFiles.add(file); } else if (file.isDirectory()) { findOutAllLegalFilesRecursionWithDirection(file); } } } private boolean isLegalFileName (String fileName) { for (String suffix : suffixes) { if (fileName.endsWith(suffix)) { return true ; } } return false ; } private void countEachLegalFile () { printStartFriendInfo(); for (File file : legalFiles) { try { BufferedReader bufferedReader = new BufferedReader (new FileReader (file)); int countLine = 0 ; while (bufferedReader.readLine() != null ) { countLine++; } getLog().info(file.getAbsolutePath() + ": " + countLine + (countLine > 1 ? " lines" : " line" )); } catch (IOException e) { getLog().error("Can't open the file " + file.getName()); } } printEndFriendInfo(); } private void printStartFriendInfo () { getLog().info("" ); getLog().info(" COUNT LINE" ); getLog().info("" ); } private void printEndFriendInfo () { getLog().info("" ); getLog().info("" ); } }
2.3 将插件部署到本地
运行如下命令
成功build,但是出现如下error以及warning
1 2 3 4 5 6 7 8 9 10 [ERROR] Artifact Ids of the format maven-___-plugin are reserved for plugins in the Group Id org.apache.maven.plugins Please change your artifactId to the format ___-maven-plugin In the future this error will break the build. [WARNING] Goal prefix is specified as: 'maven-count-plugin'. Maven currently expects it to be 'count'.
[ERROR]
:我们之前设定的artifactId为maven-count-plugin
,这种形式是保留给groupId为org.apache.maven.plugins
的插件的
[WARNING]
:自动生成的pom文件中有如下一句<goalPrefix>maven-count-plugin</goalPrefix>
,即前缀名称为maven-count-plugin,这里Maven建议前缀可以修改为count
于是,修改pom文件,将artifactId改为count-maven-plugin
,且前缀改为count
然后在运行命令进行部署
2.4 使用插件
在另一个项目的pom文件中增加如下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <project > ... <build > <plugins > <plugin > <groupId > org.liuyehcf</groupId > <artifactId > count-maven-plugin</artifactId > <version > 1.0-SNAPSHOT</version > <executions > <execution > <id > count-line</id > <phase > compile</phase > <goals > <goal > count</goal > </goals > <configuration > <suffixes > <suffix > .java</suffix > <suffix > .xml</suffix > </suffixes > <baseDir > .</baseDir > </configuration > </execution > </executions > </plugin > </plugins > </build > ... </project >
执行以下命令
得到如下输出
1 2 3 4 5 6 7 8 9 10 11 12 ... --- count-maven-plugin:1.0-SNAPSHOT:count (count-line) @ SampleMaven --- [INFO] baseDir: /Users/HCF/Documents/tmp/SampleMaven [INFO] [INFO] COUNT LINE [INFO] [INFO] /Users/HCF/Documents/tmp/SampleMaven/pom.xml: 111 lines [INFO] /Users/HCF/Documents/tmp/SampleMaven/src/test/java/org/liuyehcf/SampleTest.java: 24 lines [INFO] /Users/HCF/Documents/tmp/SampleMaven/src/main/java/org/liuyehcf/Sample.java: 9 lines [INFO] [INFO] ...
3 注解
3.1 @Mojo
@Mojo注解包含如下属性
name
:这是唯一必须声明的属性,即目标(goal)的名称
defaultPhase
:默认绑定的阶段
其他属性不详细罗列了
3.2 @Parameter
@Parameter注解包含如下属性
property
:指定参数名称
defaultValue
:默认值
required
:该属性是否必须配置
readonly
:只读属性,不可修改
4 错误处理和日志
AbstractMoj实现了Mojo接口,execute()方法会抛出两种异常:
MojoExecutionException
MojoFailureException
当Maven执行插件目标的时候遇到MojoFailureException,就会显示"BUILD FAILURE"的错误信息,这种异常表示Mojo在运行时发现了预期的错误
当Maven执行插件目标的时候遇到MojoExecutionException,就会显示"BUILD ERROR"的错误信息,这种异常表示Mojo在运行时发现了未预期的错误
此外,AbstractMoj提供了一个getLog()方法,用户可以使用该方法获得一个Log对象
5 测试插件
测试插件有两种方式
手动测试
集成测试
手动测试无非就是将插件安装到本地仓库后,然后再找个项目测试该插件
集成测试需要一个实际的Maven项目,配置该项目使用插件,然后在该项目上运行Maven构建,最后再验证该构建成功与否。Maven社区有一个来帮助插件集成测试的插件,就是maven-invoker-plugin
,该插件能够用来在一组项目上执行Maven,并检查每个项目是否构建成功,最后,它还可以执行BeanShell或Groovy脚本来验证项目构建的输出
6 参考