自制Maven插件
自制Maven插件
参看maven/maven.in.action/ch-17
的代码
一般步骤:
-
mvn archetype:generate
,然后选择maven-archtype-plugin (An archtype which contains a sample Maven plugin.)
-
编写插件的目标,称为Mojo类,继承自
AbstractMojo
。 - 提供配置点。
- 实现目标的行为。
- 定义错误及异常时Maven的行为。
- 测试
以下用一个统计代码行数的插件作为例子。
创建项目
mvn archetype:generate
然后选择:
maven-archtype-plugin (An archtype which contains a sample Maven plugin.)
生成pom.xml
:
- packaging必须为maven-plugin
-
依赖
maven-plugin-api
版本要和maven版本保持一致。
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>maven-loc-plugin</artifactId> <packaging>maven-plugin</packaging> <version>0.0.1-SNAPSHOT</version> <name>Maven LOC Plugin</name> <url>http://www.juvenxu.com/</url> <properties> <maven.version>3.0-beta-1</maven.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>${maven.version}</version> </dependency> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-model</artifactId> <version>${maven.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </build> </project>
编写目标
Archetype生成的会有一个默认的MyMojo.java,删除掉它生成一个自己的CountMojo
:
-
继承
AbstractMojo
,类上注释@goal count
表示这是count目标 -
@parameter
表示是可以配置的参数 -
execute()
方法里是行为。
/** * Goal which counts lines of code of a project * * @goal count */ public class CountMojo extends AbstractMojo { private static final String[] INCLUDES_DEFAULT = { "java", "xml", "properties" }; /** * @parameter expression="${project.basedir}" * @required * @readonly */ private File basedir; /** * @parameter expression="${project.build.sourceDirectory}" * @required * @readonly */ private File sourceDirectory; /** * @parameter expression="${project.build.testSourceDirectory}" * @required * @readonly */ private File testSourceDirectory; /** * @parameter expression="${project.build.resources}" * @required * @readonly */ private List<Resource> resources; /** * @parameter expression="${project.build.testResources}" * @required * @readonly */ private List<Resource> testResources; /** * The file types which will be included for counting * * @parameter */ private String[] includes; public void execute() throws MojoExecutionException { if ( includes == null || includes.length == 0 ) { includes = INCLUDES_DEFAULT; } try { countDir( sourceDirectory ); countDir( testSourceDirectory ); for ( Resource resource : resources ) { countDir( new File( resource.getDirectory() ) ); } for ( Resource resource : testResources ) { countDir( new File( resource.getDirectory() ) ); } } catch ( IOException e ) { throw new MojoExecutionException("Unable to count lines of code.", e); } } private void countDir( File dir ) throws IOException { /* ... */ } }
注释有@parameter
的表示是可以配置的参数,比如includes
就是可以配置的:
<plugin> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>maven-loc-plugin</artifactId> <version>0.0.1-SNAPSHOT</version> <configuration> <includes> <include>java</include> <include>sql</include> </includes> </configuration> </plugin>
这样的插件就可以在maven中使用了,可以加在pom文件里,也可以从命令行调用:
mvn com.juvenxu.mvnbook:maven-loc-plugin:0.0.1-SNAPSHOT:count
放在setting.xml
里:
<settings> <pluginGroups> <pluginGroup>com.juvenxu.mvnbook</pluginGroup> </pluginGroups> </settings>
以后就可以简化:
vmn loc:count
Mojo标注
-
@goal <name>
:目标名称,命令行调用与pom中都要用。 -
@phase <phase>
:默认绑定到default阶段 -
*
@requiresDependencyResolution <compile/test/runtime>
:默认要解析完runtime的依赖。 -
@requiresProject <true/false>
:默认要在一个项目里才能执行 -
@requiresDirectInvocation <true/false>
:默认不是只能在命令行调用执行 -
@requiresOnline <true/false>
:默认不用在线状态 -
@requiresReport <true/false>
:默认不要求报告已经生成 -
@aggregater
:在有多个子模块时默认只在顶层模块运行,如生成javadoc -
@execute goal="<goal>"
:在运行前选运行另一个目标。如果是本插件的目标直接用 目标名,其他插件的格式为prefix:goal
-
@execute phase="<phase>"
:先要运行某阶段 -
@execute lifecycle="<lifecycle>" phase="<phase>"
:
自定义生命周期配置文件位于:src/main/resources/META-INF/maven/lifecycle.xml
:
<lifecycles> <lifecycles> <id>surefire</id> <phases> <phase> <id>test</id> <configuration> <testFailureIgnore>true</testFailureIgnore> </configuration> </phase> </phases> </lifecycles> </lifecycles>
Mojo参数
参数类型
- boolean:Boolean boolean
- int: Integer int Long long Short short Byte byte
- float:Float float Double double
- String:StringBuffer Character char
-
Data:
yyyy-MM-dd HH:mm:ss.s a
或yyyy-MM-dd HH:mm:ssa
。 例:<sampleDate>2010-06-06 3:14:55.1 PM</sampleDate>
或:<sampleDate>2010-06-06 3:14:55PM</sampleDate>
-
File:
private File sampleFile
,<sampleFile>/tmp/aa.txt</sampleFile>
-
URL:
private URL myURL
,<myURL>http://www.aa.com</myURL>
-
数组:
<includes><include>111</include><include>222</include></includes>
- Collection的实现。
-
Map:
<sampleMap><key1>aaa</key1><key2>bbb</key2></sampleMap>
-
Properties:
<sampleProperties><preperty><name>attr1</name><value>val1</value></preperty><preperty><name>attr2</name><value>val2</value></preperty></sampleProperties>
parameter标签
@readonly
只读
@required
必填
@parameter alias="<aliasName>"
,给长参数起别名:
/** * @parameter alias="uid" */ private String uniqueIdentity
<uid>juven</uid>
@parameter alias="${aSystemProperty}"
:取系统参数
/** * @parameter expression="${maven.test.skip}" */ private boolean skip;
@parameter default-value="aValue/${anExpression}"
:带默认值的
/** * @parameter defaultValue="true" */ private boolean skip;
或对于必填项,就是默认值:
/** * @parameter expression="${maven.test.skip}" * @required */ private boolean skip;
错误处理和日志
-
抛
MojoFailureException
,可预期的错误,如编译时失败 -
抛
MojoExecutionException
,不可预期的错误,如IO错误
错误日志,通过getLog()
方法:
-
debug:默认不显示,
-X
参数打开 - info
- wran
- error
测试Maven插件
使用脚本语言如BeanShell或Groovy比一般的测试方式更加方便。比如用
maven-invoker-plugin
来调用groovy脚本:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-invoker-plugin</artifactId> <version>1.5</version> <configuration> <projectsDirectory>src/it</projectsDirectory> <goals> <goal>install</goal> </goals> <postBuildHookScript>validate.groovy</postBuildHookScript> </configuration> <executions> <execution> <id>integration-test</id> <goals> <goal>install</goal> <goal>run</goal> </goals> </execution> </executions> </plugin>
再src目录下面再建立一个新的测试项目POM:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu</groupId> <artifactId>app</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>app</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>maven-loc-plugin</artifactId> <version>0.0.1-SNAPSHOT</version> <executions> <execution> <goals> <goal>count</goal> </goals> <phase>verify</phase> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
验证的groovy脚本,直接读日志文件,检查是否有匹配的行:
def file = new File(basedir, 'build.log') def countMain = false def countTest = false file.eachLine { if ( it =~ /src.main.java: 13 lines of code in 1 files/ ) countMain = true if ( it =~ /src.test.java: 38 lines of code in 1 files/ ) countTest = true } if ( !countMain ) throw new RuntimeException( "incorrect src/main/java count info" ); if ( !countTest ) throw new RuntimeException( "incorrect src/test/java count info" );
maven-invoker-plugin还可以有其他的配置点,比如
-
debug(boolean)
:是否打开debug日志。 -
settingsFile(File)
:用哪个settings.xml
默认是本机的settings.xml
。 -
localRepositoryPath(File)
:用哪个本地仓库。 -
preBuildHookScript(String)
:构建前运行的BeanShell或Groovy脚本 -
postBuildHookScript