您的位置:首页 > 编程语言 > Go语言

Maven:编写Maven组件

2016-10-18 15:46 309 查看
一、编写Maven插件步骤 
在这里,我们就一个小的项目案例为大家演示一下如何一步一步编写Maven插件。该案例实现的功能是,统计Maven项目各个源代码目录下面文件的数量,以及它们加起来共多少代码。 
1.创建maven-plugin项目:插件本身也是Maven项目,特殊的地方在于它packaging必须是maven-plugin;使用maven-archetype-plugin快速创建一个Maven项目。 
C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources>mvn archetype:generate
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.4:generate (default-cli) > generate-sources@ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.4:generate (default-cli) < generate-sources@ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.4:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
... ...
1713: remote -> us.fatehi:schemacrawler-archetype-plugin-dbconnector (-)
1714: remote -> us.fatehi:schemacrawler-archetype-plugin-lint (-)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 876: maven-archetype-plugin
Choose archetype:
1: remote -> org.apache.maven.archetypes:maven-archetype-plugin (An archetype which contains a sample Maven plugin.)
2: remote -> org.apache.maven.archetypes:maven-archetype-plugin-site (An archetype which contains a sample Maven plugin site. This archetype can be layered upon
an existing Maven plugin project.)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1
Choose org.apache.maven.archetypes:maven-archetype-plugin version:
1: 1.0
2: 1.1
3: 1.2
Choose a number: 3: 3
Define value for property 'groupId': : com.chengxiang.maven
Define value for property 'artifactId': : maven-plugin
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  com.chengxiang.maven: : maven-plugin
Confirm properties configuration:
groupId: com.chengxiang.maven
artifactId: maven-plugin
version: 1.0-SNAPSHOT
package: maven-plugin
Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: maven-archetype-plugin:1.2
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.chengxiang.maven
[INFO] Parameter: artifactId, Value: maven-plugin
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: maven-plugin
[INFO] Parameter: packageInPathFormat, Value: maven-plugin
[INFO] Parameter: package, Value: maven-plugin
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.chengxiang.maven
[INFO] Parameter: artifactId, Value: maven-plugin
[INFO] project created from Archetype in dir: C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:31 min
[INFO] Finished at: 2016-10-16T23:39:06+08:00
[INFO] Final Memory: 14M/172M
[INFO] ------------------------------------------------------------------------
2.为插件编写目标、配置点和目标行为,处理相关错误日志:每个插件都包含一个或者多个目标,故必须提供一个或者多个继承AbstractMojo类;可视具体情况提供可配置的参数;根据具体目标实现execute()方法逻辑; 
/**
* CountMojo:每个插件的目标类都需要继承AbstractMojo,并实现execute()方法
* Created by chengxiang.peng on 2016/10/17.
* "@goal":标注来写明自己的目标名称
* @goal count
*/
public class CountMojo extends AbstractMojo {
//默认统计Java,xml和properties文件
private static final String[] INCLUDES_DEFAULT = {"java", "xml", "properties"};

/**
* "expression":从系统属性读取这几个字段;"@readonly":不允许用户进行配置
* 项目基础目录
* @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;

/**
* "@parameter":使用该插件的时候,可以配置该参数
* 计算的代码行数的类型范围
* @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) {
//异常转译,将IO异常转换成Mojo定义的异常抛出
throw new MojoExecutionException("Unable to count lines of code.", e);
}
}

/**
* 统计指定目录下文件的代码行数
* @param dir 统计的指定目录
* @throws IOException 不处理IO异常,抛出给外部处理
*/
private void countDir(File dir) throws IOException {
if (!dir.exists()) {
return;
}
//查找目标目录中指定类型的文件集合
List<File> collected = new ArrayList<File>();
collectFiles(collected, dir);
//统计目标类型文件的代码行数
int lines = 0;
for (File sourceFile : collected) {
lines += countLine(sourceFile);
}
//输出统计结果:AbstractMojo的getLog()方法返回日志对象,输出Maven日志
String path = dir.getAbsolutePath().substring(basedir.getAbsolutePath().length());
getLog().info(path + ": " + lines + " lines of code in " + collected.size() + " files");
}

/**
* 遍历查找目标目录了中指定类型的文件,并保存在集合中
* @param collected 指定类型文件集合
* @param file 目标目录
*/
private void collectFiles(List<File> collected, File file) {
if (file.isFile()) {
//如果是文件,则判断是否为指定类型
for (String include : includes) {
if (file.getName().endsWith("." + include)) {
collected.add(file);
break;
}
}
} else {
//如果是目录,则遍历子文件或者子目录,递归查找
for (File sub : file.listFiles()) {
collectFiles(collected, sub);
}
}
}

/**
* 统计指定文件的代码行数
* @param file 统计目标文件
* @return 代码行数
* @throws IOException 不处理IO异常,抛出给外部处理
*/
private int countLine(File file) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(file));
int line = 0;
try {
while (reader.ready()) {
reader.readLine();
line++;
}
} finally {
reader.close();
}
return line;
}
}
3.上传和测试插件:适用mvn clean install命令,将编写完成的插件,上传在本地库中,然后在实际运行该插件验证其行为; 
C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin>mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building mavenplugin Maven Plugin 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ mavenplugin ---
[INFO] Deleting C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ mavenplugin ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ mavenplugin ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\target\classes
[INFO]
[INFO] --- maven-plugin-plugin:3.2:descriptor (default-descriptor) @ mavenplugin ---
[INFO] Using 'UTF-8' encoding to read mojo metadata.
[INFO] Applying mojo extractor for language: java
//Maven版本升级,@parameter expression="${property}"写法过时,可升级按照说明更新写法
[WARNING] mavenplugin.CountMojo#basedir:
[WARNING]   The syntax
[WARNING]     @parameter expression="${property}"
[WARNING]   is deprecated, please use
[WARNING]     @parameter property="property"
[WARNING]   instead.
[WARNING] mavenplugin.CountMojo#resources:
[WARNING]   The syntax
[WARNING]     @parameter expression="${property}"
[WARNING]   is deprecated, please use
[WARNING]     @parameter property="property"
[WARNING]   instead.
[WARNING] mavenplugin.CountMojo#sourceDirectory:
[WARNING]   The syntax
[WARNING]     @parameter expression="${property}"
[WARNING]   is deprecated, please use
[WARNING]     @parameter property="property"
[WARNING]   instead.
[WARNING] mavenplugin.CountMojo#testResources:
[WARNING]   The syntax
[WARNING]     @parameter expression="${property}"
[WARNING]   is deprecated, please use
[WARNING]     @parameter property="property"
[WARNING]   instead.
[WARNING] mavenplugin.CountMojo#testSourceDirectory:
[WARNING]   The syntax
[WARNING]     @parameter expression="${property}"
[WARNING]   is deprecated, please use
[WARNING]     @parameter property="property"
[WARNING]   instead.
[INFO] Mojo extractor for language: java found 1 mojo descriptors.
[INFO] Applying mojo extractor for language: bsh
[INFO] Mojo extractor for language: bsh found 0 mojo descriptors.
[INFO] Applying mojo extractor for language: java-annotations
[INFO] Mojo extractor for language: java-annotations found 0 mojo descriptors.
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ mavenplugin ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ mavenplugin ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ mavenplugin ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ mavenplugin ---
[INFO] Building jar: C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\target\mavenplugin-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-plugin-plugin:3.2:addPluginArtifactMetadata (default-addPluginArtifactMetadata) @ mavenplugin ---
[INFO]
//插件构建完毕后,上传到本地仓库后
[INFO] --- maven-install-plugin:2.4:install (default-install) @ mavenplugin ---
[INFO] Installing C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\target\mavenplugin-1.0-SNAPSHOT.jar to C:\Users\chengxiang.peng.QUNARSERVERS\.m2\repository\com\chengxiang\mavenplugin\1.0-SNAPSHOT\mavenplugin-1.0-SNAPSHOT.jar
[INFO] Installing C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin\pom.xml to C:\Users\chengxiang.peng.QUNARSERVERS\.m2\repository\com\chengxiang\mavenplugin\1.0-SNAPSHOT\mavenplugin-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.798 s
[INFO] Finished at: 2016-10-17T17:54:56+08:00
[INFO] Final Memory: 20M/171M
[INFO] ------------------------------------------------------------------------
在当前的项目目录下,执行编写插件的目标; 
C:\Users\chengxiang.peng.QUNARSERVERS\MavenSources\mavenplugin>mvn com.chengxiang:mavenplugin:1.0-SNAPSHOT:count
[INFO] Scanning for projects...
Downloading: http://svn.corp.qunar.com:8081/nexus/content/groups/public/com/chengxiang/mavenplugin/1.0-SNAPSHOT/maven-metadata.xml Downloading: http://l-digging7.wap.cn8.qunar.com:8086/content/repositories/thirdparty/com/chengxiang/mavenplugin/1.0-SNAPSHOT/maven-metadata.xml [INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building mavenplugin Maven Plugin 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
//插件输出结果
[INFO] --- mavenplugin:1.0-SNAPSHOT:count (default-cli) @ mavenplugin ---
[INFO] \src\main\java: 165 lines of code in 1 files
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.472 s
[INFO] Finished at: 2016-10-17T18:17:31+08:00
[INFO] Final Memory: 7M/155M
[INFO] ------------------------------------------------------------------------
二、Mojo标注
标注 说明 备注 
@goal<name> 目标名称 唯一必须声明;命令调用、POM配置插件时候使用; 
@phase<phase> 目标绑定的生命周期阶段 默认至Default阶段 
@requiresDependencyResolution<scope> 运行目标前必须解析所有指定范围的依赖 如maven-surefire-plugin的test带有@requiresDependencyResolution test标识在执行测试之前,所有测试范围的依赖都必须解析 
@requiresProject<true/false> 目标是否必须在一个Maven项目中运行 默认true,如maven-help-plugin的system目标,显示系统属性和环境变量,不需要实际项目 
@requiresDirectInvocation<true/false> 目标只能通过命令调用 默认为false,如果视图在POM中将其绑定到生命周期阶段,Maven就会报错 
@requiresOnline<true/false> 是否要求Maven必须在线状态 默认false 
@requiresReport<true/false> 是否要求项目报告已经生成 默认false 
@aggregator 当在多模块项目运行时,标识䯮只会在顶层模块运行 如maven-javadoc-plugin的aggregator-jar使用了该标注,多模块时只会在顶层项目生成一个聚合的javadoc文档 
@execute goal="<goal>" 在运行目标前先让maven运行另外一个目标 如果是本插件的目标,则直接使用目标名称,否则使用“prefix:goal”形式 
@execute phase="<phase>" 在运行目标运行一个并行的生命周期,到指定的阶段为止。 如maven-dependency-plugin的analyze使用了标注@execute phase="test-compile",当执行dependency:analyze的时候,会首先执行default生命周期至test-compile阶段 
@execute lifecycle="<lifecycle>" phase="<phase>" 运行目标之前先运行一个自定义的生命周期,到指定的阶段为止。 如maven-surefire-report-plugin插件report目标,标注了@execute phase="test" lifecycle="surefire",表示运行这个自定义的surefire生命周期至test阶段 
三、Mojo参数 
Maven支持种类多样的Mojo参数,如下: 
1.boolean(包括boolean和Boolean): 
/**
*@parameter
*/
private boolean sampleBoolean;
配置:
<sampleBoolean>true</sampleBoolean>
2.int(包括Integer、long、Long、short、Short、byte、Byte): 
/**
*@parameter
*/
private int sampleInt;
配置:
<sampleInt>8</sampleInt>
3.float(包括Float、double、Double): 
/**
*@parameter
*/
private int sampleFloat;
配置:
<sampleFloat>8.8</sampleFloat>
4.String(包括StringBuffer、char、Character): 
/**
*@parameter
*/
private String sampleString;
配置:
<sampleString>8</sampleString>
5.Date(格式为yyy-MM-dd HH:mm:ss Sa) 
/**
*@parameter
*/
private Date sampleDate;
配置:
<sampleDate>2010-06-06 3:14:55.1 PM</sampleDate>
6.File 
/**
*@parameter
*/
private File sampleFile;
配置:
<sampleFile>c:\tmp</sampleFile>
7.URL 
/**
*@parameter
*/
private URL sampleURL;
配置:
<sampleURL>http://www.qunar.com</sampleURL>
8.数组 
/**
*@parameter
*/
private String[] includes;
配置:
<includes>
<include>java</include>
<include>sql</include>
</includes>
9.Collection(任何实现Collection接口的类,如ArrayList和HashSet) 
/**
*@parameter
*/
private List includes;
配置:
<includes>
<include>java</include>
<include>sql</include>
</includes>
10.Map 
/**
*@parameter
*/
private Map sampleMap;
配置:
<sampleMap>
<key>value1</key>
<key>value2</key>
</sampleMap>
11.Properties 
/**
*@parameter
*/
private Properties sampleProperties;
配置:
<sampleProperties>
<samplePropertie>
<name>p_name_1</name>
<value>p_value_1</value>
</samplePropertie>
<samplePropertie>
<name>p_name_2</name>
<value>p_value_2</value>
</samplePropertie>
</sampleProperties>
12.@parameter alias="<aliasName>" 
可以为任何Mojo参数使用别名,如: 
/**
*@parameter alias="uid"
*/
private String uniqueIndentity;
配置:
<uid>user_1</uid>
13.@parameter expression="${aSystemProperty}" 
使用系统表达式对Mojo参数进行赋值,则无法在命令行直接配置,如: 
/**
*@parameter expression="${maven.test.skip}"
*/
private boolean skip;
14.@parameter defaultValue="aValue/${anExpression}" 
为用户设置的Mojo参数提供一个默认值,如: 
/**
*@parameter defaultValue="true"
*/
private boolean sampleBoolean;
15.@readonly 
Mojo参数是只读的,无法对其进行配置; 
16.required 
Mojo参数是必须的,没有配置该Mojo参数且没有默认值,Maven就会报错; 
四、错误处理和日志 
AbstractMojo实现Mojo接口,execute()方法定义如下: 
void execute() throws MojoExecutionException,MojoFailureException; 
    1.MojoExecutionException:显示“BUILD FAILURE”错误信息,表示Mojo运行时发现了预期的错误; 
    2.MojoFailureException:显示“BUILD ERROR”错误信息,表示Mojo运行时发现了未预期的错误; 
上述两种异常能够在Mojo执行出错的时候提供一定信息,但这往往是不够的。用户在编写插件的时候还应该提供足够的日志信息:AbstractMojo提供了一个getLog()方法,用户可以使用 
    1.debug:调试级别日志,默认不会输出,可以在执行mvn命令使用-X参数打开,帮助程序员了解插件具体运行状态,尽量详细; 
    2.info:消息级别日志,默认输出,简洁,了解插件重要运行状态; 
    3.warn:警告级别日志,插件运行时遇到了一些问题或错误,会导致运行失败,应该尽快修复; 
    4.error:错误级别日志,插件运行的遇到了一些问题或错误,导致Mojo无法继续运行,使用该级别日志提供详细的错误; 
上述级别的日志都提供了一个方法,以debug为例,分别为: 
    void debug(CharSequence content); 
    void debug(CharSequence content,Throwable error); 
    void debug(Throwable error); 
 
新技术,新未来!欢迎大家关注“1024工场”微信服务号,时刻关注我们的最新的技术讯息!(甭客气!尽情的扫描或者长按!)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息