【Maven】-生命周期与插件

xiaoxiao2021-02-28  7

maven插件生命周期

Maven作为一个构建工具由于遵循了约定优于配置的原则,只要编写比Ant所需少得多的脚本就能实现同样的构建。并且Maven还有很多Ant没有的高级特性,例如依赖管理等,这一切使得Maven不仅是构建工具,更是项目管理工具,并越发流行起来。

由于Maven在使用时非常简单,比如下面是百度百科中对Maven常用命令的列表:

mvn archetype:create 创建Maven项目

mvn compile 编译源代码

mvn deploy 发布项目

mvn test-compile 编译测试源代码

mvn test 运行应用程序中的单元测试

mvn site 生成项目相关信息的网站

mvn clean 清除项目目录中的生成结果

mvn package 根据项目生成的jar

mvn install 在本地Repository中安装jar

mvn eclipse:eclipse 生成eclipse项目文件

mvn jetty:run 启动jetty服务

mvn tomcat:run 启动tomcat服务

 

开发人员往往只需要执行几个命令就能达成日常需要完成的目标,这是一个优点,但正是由于看起来太过简单了,可能会导致开发人员对待Maven不求甚解,比如在上面这一列表中有的命令有:(冒号),有的则没有,这些不同到底意味着什么,也许有的开发人员并不关心,这也可以理解,毕竟开发人员更关注于开发代码。但是知道Maven的一些基本概念还是有利于我们熟练掌握Maven的,像Maven这样的好东西,不仅会用而且能用好才是理想状态。刚才提到的有冒号和没冒号命令的区别,简单来的说,有冒号的命令是直接执行指定的插件,没有冒号的命令是按照Maven生命周期来执行的。但是什么是生命周期,插件又是什么呢?别着急,这正是本文要介绍的内容。

1.Maven的生命周期

Maven的生命周期是对所有的构建过程进行抽象和统一。Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,生命周期只是定义了一系列的阶段,并确定这些阶段的执行顺序。而在执行这些阶段时,实际的工作还是由插件来完成的。这种思想与设计模式中的模板方法非常相似。

Maven有三套相互独立的生命周期,分别是clean、default和site。clean生命周期的目的是清理项目,default生命周期的目的是构建项目,而site生命周期的目的是建立项目站点。每个生命周期都定义了有顺序的阶段(phase),如下表:

生命周期cleandefaultsite阶段(phase), 执行顺序由上至下pre-cleanvalidatepre-sitecleaninitializesitepost-cleangenerate-sourcespost-site process-sourcessite-deploygenerate-resources process-resourcescompileprocess-classesgenerate-test-sourcesprocess-test-sourcesgenerate-test-resourcesprocess-test-resourcestest-compileprocess-test-classestestprepare-packagepackagepre-integration-testintegration-testpost-integration-testverifyinstalldeploy

 

用户在mvn命令后可以指定三个生命周期中的任何阶段,则Maven会按以下逻辑执行:首先会得到该阶段所属生命周期,从该生命周期中的第一个阶段开始按顺序执行,直至该阶段本身。例如执行mvn clean命令会依次执行clean生命周期中的pre-clean阶段及clean阶段。mvn命令后可以指定多个阶段,Maven会按照输入的顺序依次执行,每次执行都会按照之前描述的逻辑执行。

之前提到实际的工作还是由插件来完成的,这意味着插件需要和阶段绑定起来。Maven已经事先将很多核心插件绑定到对应的阶段,这样用户几乎不用配置就有构建Maven项目。Maven的内置绑定如下:

生命周期阶段(phase)插件目标cleancleanmaven-clean-plugin:cleandefaultprocess-resourcesmaven-resources-plugin:resourcescompilemaven-compiler-plugin:compilegenerate-test-resourcesmaven-resources-plugin:testResoucestest-compilemaven-compiler-plugin:testCompiletestmaven-surefire-plugin:testpackage打包类型是jar时:maven-jar-plugin:jar; 打包类型是war时:maven-war-plugin:warinstallmaven-install-plugin:installdeploymaven-deploy-plugin:deploysitesitemaven-site-plugin:sitesite-deploymaven-site-plugin:deploy

细心的同学可能会问插件目标是什么呢,为什么不直接绑定到插件上。这是因为一个插件往往有多个功能,而每一个功能就是一个插件目标。回到文章开头提到的问题,像mvn archetype:create这样的命令,archetype:create实际上就是一个插件目标,mvn archetype:create这条命令会直接执行指定的插件目标,并不会执行其它任何的插件目标,这和执行生命周期阶段不一样。

除了内置绑定外,用户还可以自己选择将某个插件目标绑定到生命周期的某个阶段,从而让构建过程更为完善。

例如,虽然maven已经将test阶段和maven-surefire-plugin:test插件目标绑定起来,但却没有将integration-test阶段和任何插件目标绑定。如果我们想在test阶段只执行单元测试,而在integration-test阶段进行集成测试的话,可以如下配置:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.10</version> <configuration> <skip>true</skip> </configuration> <executions> <execution> <id>run-test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> <configuration> <skip>false</skip> <includes> <include>**/unit/**/*.java</include> </includes> </configuration> </execution> <execution> <id>run-integration-test</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <skip>false</skip> <includes> <include>**/integration/**/*.java</include> </includes> </configuration> </execution> </executions> </plugin>

其中红色背景的<skip>true</skip>是为了让Maven的默认绑定(test阶段<->maven-surefire-plugin:test插件目标) 无效 (其实绑定仍然有效,只是执行时忽略执行罢了),而后面的executions块内容则增加了两个绑定,分别将maven-surefire-plugin:test插件目标绑定到test阶段和integration-test阶段,只是配置不一样了,分别执行unit包和integration包下的测试类。

2.Maven的插件

其实在刚才介绍生命周期中已经提到了可以在命令行的mvn命令后直接指定插件目标,之所以Maven支持这种方式是因为有些任务不适合绑定到生命周期上。在命令行调用插件的格式如下 :

mvn groupId:artifactId:version:goal

其中groupId、artifactId、version共同表示了插件的坐标;goal则表示插件目标的方法。

但我们看到很多执行插件目标的格式与之并不相符,例如文章开头的mvn archetype:create,archetype并不是groupId、artifactId或version而是插件的前缀,这就有了第二种调用插件的格式:

mvn 前缀:goal

Maven是如何解析插件的前缀的呢?实际是Maven是通过查询插件仓库的元数据才得知插件前缀对应插件的groupId、artifactId,而如果插件是Maven的核心插件则在超级POM中已经定义了插件的版本,如果不是核心插件,则默认取最新的release版本。

Maven的插件仓库默认是http://repo1.maven.org/maven2/org/apache/maven/plugins/和http://repository.codehaus.org/org/codehaus/mojo/,相应的查询插件仓库元数据时会默认使用org.apache.maven.plugins和org.codehaus.mojo两个groupId。但也可以通过配置settings.xml让Maven检查其他groupId上的插件仓库元数据,如:

<settings> <pluginGroups> <pluginGroup>com.your.plugins</pluginGroup> </pluginGroups> </settings>

 

Maven为我们提供了丰富的插件资源,使得开发调试过程中非常方便,可以满足大多数场景下的需求。当然有时候,我们也需要根据需求定制自己的插件。下面是在开发Maven插件时的一点备忘录,具体的开发流程请Google,有不少的教程,这里只是概述一下,同时记录一些容易掉坑的点。

 

1,Maven的工具由一个又一个的插件组成的,插件类继承了AbstractMojo类,需要覆写execute方法。getLog()获取的是AbstractMojo内部的log,类型是:org.apache.maven.plugin.logging.Log; 在Mojo的开发中,不要使用其他的Log基础设施。注: Mojo = Maven Old Java Object;

1

2

3

4

5

public class Example extends AbstractMojo{ 

    public void execute() throws MojoExecutionException, MojoFailureException { 

        getLog().info("Hello world"); 

    } 

}

2,pom.xml文件中,打包(packaging)类型应该为 maven-plugin,而不是war/jar。同时需要依赖的两个核心依赖是: 3,开发Maven插件和开发普通的Maven工程的流程是一样的,不同在于插件的 archetype应该设置为:maven-archetype-plugin;

maven-plugin-api:这是插件开发的api;maven-plugin-annotations:这是进行插件注解的api。

4,插件的入口类为继承了AbstractMojo的实现类,但是需要进行注解。注解@Mojo是必须要的,这是定义插件对象的启动方法,由于该类只有一个方法,所以启动方法和启动类是一致的。在Maven 3之前是使用注释注解:@goal doSomething这种方式。现在已经不使用这种方式了。举个例子,如果进行如下标注:

1

2

3

4

@Mojo( name = "doSomething")

public class GreetingMojo extends AbstractMojo{

...

}

那么运行的时候使用如下方式执行:

1

mvn groupId:artifactId:version:doSomething  //如果一个项目中只有一个@Mojo标记,则verison可以省略

5,除了标注执行的方法,还可对成员变量进行标注,以便自动获取或由用户传入参数。

1

2

3

4

5

@Parameter( property = "greeting", defaultValue = "Hello World!" )   //这是由用户传入的参数,可以在命令行中由-D参数传入

private String greeting;

  

@Parameter( expression = "${project}", defaultValue = "${project}" )   //还可以使用表达式,引用Maven工程中定义好的变量

private String project;

6,每个Mojo(由@Mojo标注)都有生命周期,即该Mojo的执行时间。用户可以选择将某个插件目标绑定到生命周期的某个阶段,从而让构建过程更为完善。生命周期的具体介绍参考文末给出的文章。下面将插件配置到编译阶段执行:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<build>

    <plugins>

      <plugin>

        <groupId>sample.plugin</groupId>

        <artifactId>doSomething-maven-plugin</artifactId>

        <version>1.0-SNAPSHOT</version>

        <executions>

          <execution>

            <phase>compile</phase>

            <goals>

              <goal>doSomething</goal>

            </goals>

          </execution>

        </executions>

      </plugin>

    </plugins>

</build>

 

 

代码行插件开发

定义 mojo类

package com.yon.it.maven_demo_plugin; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.management.modelmbean.RequiredModelMBean; import org.apache.log4j.lf5.util.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.Execute; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @Mojo(name="count" , defaultPhase = LifecyclePhase.COMPILE) public class CountLineMojo extends AbstractMojo{ private static final String[] DEFAULT_FILES = {"java", "xml", "properties"}; @Parameter(defaultValue = "${project.basedir}", readonly = true) private File baseDir; @Parameter(defaultValue = "${project.build.sourceDirectory}", readonly = true) private File srcDir; @Parameter(defaultValue = "${project.build.testSourceDirectory}", readonly = true) private File testSrcDir; @Parameter(defaultValue = "${project.build.resources}", readonly = true) private List<Resource> resources; @Parameter(defaultValue = "${project.build.testResources}", readonly = true) private List<Resource> testResources; @Parameter(property = "count.file.includes") private String[] includes; private Log logger = getLog(); public void execute() throws MojoExecutionException, MojoFailureException { if(includes==null||includes.length==0){ includes=DEFAULT_FILES; } List<File> file=new ArrayList<File>(); FileDealWiths fiDealWiths=new FileDealWiths(includes); List<File> countFile = fiDealWiths.countFile(this.srcDir); List<File> countFile2 = fiDealWiths.countFile(this.testSrcDir); getLog().debug(this.srcDir+"文件 main/java:"+countFile.size()); getLog().debug(this.testSrcDir+"文件 main/test:"+countFile2.size()); getLog().debug(this.testSrcDir+"文件 resourcest:"+resources.size()); getLog().debug(this.testSrcDir+"文件 testResources:"+testResources.size()); int countFiles = fiDealWiths.countFiles(countFile); int countFiles2 = fiDealWiths.countFiles(countFile2); int hfd=countFiles+countFiles2; getLog().info("插件执行了:"+"代码行数"+hfd); } } package com.yon.it.maven_demo_plugin; import java.awt.image.BufferStrategy; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileDealWiths { private static Logger logger=LoggerFactory.getLogger(FileDealWiths.class); private int codeline; private String include[]; public int getCodeline() { return codeline; } public void setCodeline(int codeline) { this.codeline = codeline; } public FileDealWiths(String include[]){ this.include=include; } public List<File> countFile(File path){ List<File> li=new ArrayList<File>(); File[] listFiles = path.listFiles(); for(File f:listFiles){ if(f.isDirectory()){ List<File> countFile = countFile(f); li.addAll(countFile); }else{ li.add(f); } } return li; } /** * 读取文件 * @param file * @throws IOException */ public int readFile(File file) { int result=0; BufferedReader buf=null; try{ buf=new BufferedReader(new FileReader(file)); String line=null; while((line=buf.readLine())!=null) result=result+1; }catch(Exception exce){ logger.error("获取文件异常:"+exce.getMessage()); }finally { try { if(buf!=null)buf.close(); } catch (IOException e) { logger.error("flow error"+e.getMessage()); } } return result; } public int countFiles(List<File> files){ int conut=0; if(files.size()>0){ for(File fi:files){ String name = fi.getName(); String substring = name.substring(name.indexOf(".")); if(include.toString().contains(substring)){ conut=conut+readFile(fi); } } } return conut; } } 引入插件 <build>     <finalName>yonyon-test</finalName>         <plugins>             <plugin>                 <groupId>com.yon.it</groupId>                 <artifactId>maven-demo-plugin</artifactId>                 <version>0.0.1-SNAPSHOT</version>                 <executions>                     <execution>                         <goals>                             <goal>count</goal>                         </goals>                         <phase>package</phase>                     </execution>                   </executions>                   <configuration>                           <includes>                               <include>json</include>                               <include>java</include>                               <include>xml</include>                           </includes>                   </configuration>             </plugin>         </plugins>   </build>  
转载请注明原文地址: https://www.6miu.com/read-2150207.html

最新回复(0)