小强说Maven(二)

开头说

  这一讲来说Maven的生命周期。我们先来介绍三种标准的生命周期,接着介绍生命周期的绑定,最后是构建自定义的生命周期和生命周期扩展。



生命周期

生命周期概念

  生命周期(lifecycles)是一系列阶段(phases)的集合。就像普通的程序员的生命周期是23-25是少年阶段,25-30是青年阶段,30-35是中年阶段,35后就步入老年阶段。[手动哀声叹气–sign!]
  如果没有Maven,我们的日常工作经常是下载项目,编译,开发,测试,打包,部署等等。(我一个月薪4、5k的小小程序员,你竟然要求我做那么多!^_^)。这一套周期显然涉及各种阶段性的任务,Maven就是对以上工作抽取和统一。



三种标准生命周期

  Clean生命周期

  包含3个阶段,pre-clean、clean、post-clean。(这里的clean是阶段,不多恰巧和周期的名字重名。)
  我们敲的命令,如 mvn clean 中的clean是指的阶段。阶段具有顺序性,因此在执行clean之前会先执行pre-clean。clean阶段的具工作是通过插件来完成的。我们可以通过 help 命令来查看细节:

$ mvn help:describe -Dplugin=clean

Name: Maven Clean Plugin
Description: The Maven Clean Plugin is a plugin that removes files generated
  at build-time in a project's directory.
Group Id: org.apache.maven.plugins
Artifact Id: maven-clean-plugin
Version: 2.5
Goal Prefix: clean

This plugin has 2 goals:
clean:clean
  Description: Goal which cleans the build.
    This attempts to clean a project's working directory of the files that were
    generated at build-time. By default, it discovers and deletes the
    directories configured in project.build.directory,
    project.build.outputDirectory, project.build.testOutputDirectory, and
    project.reporting.outputDirectory.
    Files outside the default may also be included in the deletion by
    configuring the filesets tag.

clean:help
  Description: Display help information on maven-clean-plugin.
    Call
     mvn clean:help -Ddetail=true -Dgoal=<goal-name>
    to display parameter details.

For more information, run 'mvn help:describe [...] -Ddetail'

  上面说的很清楚,这些工作是由Maven Clean Plugin完成的,下面给出该插件的groupId、artifactId和version。接着说该插件有两部分(goals),一个是提供clean功能,一个是提供帮助说明。
clean生命周期和clean插件的关系
  goal可以自己执行,也可以注册到生命周期中执行。单独执行的语法是这样的(第一个clean代表插件的名称,第二个代表插件中的目标goal):

$ mvn clean:clean

  但是这里只会执行当前的目标,而若放在生命周期中执行,前面的阶段也会执行。
  下面的代码揭示插件与Clean生命周期的clean阶段的关系(id标识生命周期,phase标识阶段):

<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>default-clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>

  Clean生命周期中的pre-clean和post-clean没有做任何工作,他们的出现只是方便用户个性化配置。可以在clean前和后做自己想做的事情。

  Default生命周期

  Default生命周期包含23个阶段,如图所示:
default的23个阶段
下面对其一一说明:

  • validate: 验证pom文件
  • initialize:初始化文件目录,初始化配置文件中的属性
  • generate-sources:生成需要的源码
  • process-sources:处理源码
  • generate-resources:生成资源文件
  • process-resources:把资源文件拷贝到目标目录
  • compile:编译
  • process-classes:字节码增强处理
  • generate-test-sources:生成测试需要的源码
  • process-test-source:处理测试源码
  • generate-test-resources:生成测试需要的资源文件
  • process-test-resources:处理拷贝测试资源文件
  • test-compile:编译测试代码
  • process-test-classes:测试的字节码增强
  • test:使用特定的单元测试框架测试
  • prepare-package:组织要打包的内容
  • package:打包
  • pre-integration-test
  • integration-test:集成测试
  • post-integration-test
  • verify:验证包的有效性
  • install:写入本地资源库
  • deploy:写入远程资源库

  当然这些阶段的某些不是必要的。由于不同打包类型的阶段任务不同,特定阶段使用的插件类型也不同。比如,jar的package阶段使用的插件是maven-jar-plugin,war的package阶段使用的插件是maven-war-plugin。从下一节可以知道,Default生命周期的声明中没有配置插件,仅是定义了23个阶段。

  Site生命周期

  site生命周期用于生成项目的静态HTML。包含4个阶段:pre-site、site、post-site和site-deploy。
site生命周期



生命周期的绑定

  Maven标准的生命周期和绑定的配置位于 MAVEN_HOME/lib/maven-core-3.6.3.jar 中的 META-INF/plexus/components.xml 中(plexus是一种Ioc容器)。注意Clean生命周期和Site生命周期都有与插件的绑定配置,而Default生命周期没有。

  Default生命周期的配置如下所示:

  <component>
      <role>org.apache.maven.lifecycle.Lifecycle</role>
      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
      <role-hint>default</role-hint>
      <configuration>
        <id>default</id>
        
        <phases>
          <phase>validate</phase>
          <phase>initialize</phase>
          <phase>generate-sources</phase>
          <phase>process-sources</phase>
          <phase>generate-resources</phase>
          <phase>process-resources</phase>
          <phase>compile</phase>
          <phase>process-classes</phase>
          <phase>generate-test-sources</phase>
          <phase>process-test-sources</phase>
          <phase>generate-test-resources</phase>
          <phase>process-test-resources</phase>
          <phase>test-compile</phase>
          <phase>process-test-classes</phase>
          <phase>test</phase>
          <phase>prepare-package</phase>
          <phase>package</phase>
          <phase>pre-integration-test</phase>
          <phase>integration-test</phase>
          <phase>post-integration-test</phase>
          <phase>verify</phase>
          <phase>install</phase>
          <phase>deploy</phase>
        </phases>
        
      </configuration>
    </component>

  其中role声明接口,implementation是具体实现类。component是由role和role-hint一起标识的(role-hint不是必要的,如果role不重复的话)。

  Clean生命周期的绑定配置:

<component>
      <role>org.apache.maven.lifecycle.Lifecycle</role>
      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
      <role-hint>clean</role-hint>
      <configuration>
        <id>clean</id>
        
        <phases>
          <phase>pre-clean</phase>
          <phase>clean</phase>
          <phase>post-clean</phase>
        </phases>
        <default-phases>
          <clean>
            org.apache.maven.plugins:maven-clean-plugin:2.5:clean
          </clean>
        </default-phases>
        
      </configuration>
    </component>

  Site生命周期的绑定配置:

<component>
      <role>org.apache.maven.lifecycle.Lifecycle</role>
      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
      <role-hint>site</role-hint>
      <configuration>
        <id>site</id>
        
        <phases>
          <phase>pre-site</phase>
          <phase>site</phase>
          <phase>post-site</phase>
          <phase>site-deploy</phase>
        </phases>
        <default-phases>
          <site>
            org.apache.maven.plugins:maven-site-plugin:3.3:site
          </site>
          <site-deploy>
            org.apache.maven.plugins:maven-site-plugin:3.3:deploy
          </site-deploy>
        </default-phases>
        
      </configuration>
    </component>

  后两个生命周期中default-phase标签中包含对应阶段的默认插件配置(配置的语法是 插件包:插件名称:版本:插件的goal)。而Deffalut生命周期没有这个标签,所以还需要额外的配置。
  接下来让我们看一下jar打包方式对应的Default生命周期的各阶段插件配置:

 <component>
      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
      <role-hint>jar</role-hint>
      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
      <configuration>
        <lifecycles>
          <lifecycle>
            <id>default</id>
            
            <phases>
              <process-resources>
                org.apache.maven.plugins:maven-resources-plugin:2.6:resources
              </process-resources>
              <compile>
                org.apache.maven.plugins:maven-compiler-plugin:3.1:compile
              </compile>
              <process-test-resources>
                org.apache.maven.plugins:maven-resources-plugin:2.6:testResources
              </process-test-resources>
              <test-compile>
                org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile
              </test-compile>
              <test>
                org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
              </test>
              <package>
                org.apache.maven.plugins:maven-jar-plugin:2.4:jar
              </package>
              <install>
                org.apache.maven.plugins:maven-install-plugin:2.4:install
              </install>
              <deploy>
                org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy
              </deploy>
            </phases>
            
          </lifecycle>
        </lifecycles>
      </configuration>
    </component>

lifecycles标签标识生命周期,id标识生命周期的名字。可以看出23个阶段仅选8个阶段进行配置。



个性化生命周期

  上面讲到Default的生命周期依打包的方式变化,目前的打包种类有:jar、ear、 war、pom、rar、 par、
ejb、ejb3等等.如果我们想自己定义一种打包方式呢?这时就需要个性化的配置了。让我们先从简单的开始,这次我们仅更改Default生命周期的配置,保留原有的各阶段定义不变。以下是maven-bundle-plugin,资源路径在 https://github.com/sonatype/sonatype-bundle-plugin/
blob/master/src/main/resources/META-INF/plexus/components.xml
,它改写了Default生命周期中的package、install、deploy阶段,注意某些阶段可以包含多个插件的配置,用逗号分隔。

<component-set> 
  <components> 
    <component> 
      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>  
      <role-hint>bundle</role-hint>  
      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>  
      <configuration> 
        <lifecycles> 
          <lifecycle> 
            <id>default</id>  
            <phases> 
              <process-resources>org.apache.maven.plugins:maven-resourcesplugin: resources</process-resources>  
              <compile>org.apache.maven.plugins:maven-compilerplugin: compile</compile>  
              <process-test-resources>org.apache.maven.plugins:maven-resourcesplugin: testResources</process-test-resources>  
              <test-compile>org.apache.maven.plugins:maven-compilerplugin: testCompile</test-compile>  
              <test>org.apache.maven.plugins:maven-surefireplugin: test</test>  
              <package>org.apache.felix:maven-bundle-plugin:bundle</package>  
              <install>
                org.apache.maven.plugins:maven-installplugin: install, 
                org.apache.felix:maven-bundle-plugin:install
              </install>  
              <deploy>
                org.apache.maven.plugins:maven-deployplugin: deploy, 
                org.apache.felix:maven-bundle-plugin:deploy
              </deploy> 
            </phases> 
          </lifecycle> 
        </lifecycles> 
      </configuration> 
    </component> 
  </components> 
</component-set>

  接着我们尝试定义自己的生命周期,决定里面包含哪些阶段。

1. 首先,我们在component.xml(这个文件在 自身项目/src/main/resources/META-INF/plexus/component.xml)加入如下的内容。

<component-set> 
  <components> 
    <component> 
      <role>org.apache.maven.lifecycle.Lifecycle</role>  
      <role-hint>packt</role-hint>  
      <implementation>org.apache.maven.lifecycle.Lifecycle</implementation>  
      <configuration> 
        <id>packt_lifecycle</id>  
        <phases> 
          <phase>get-code</phase>  
          <phase>build-code</phase>  
          <phase>run-tests</phase>  
          <phase>notify</phase> 
        </phases>  
        <default-phases> 
          <get-code>com.packt:com.packt.lifecycle.sample:get-code-goal</get-code>  
          <build-code>com.packt:com.packt.lifecycle.sample:build-code-goal</build-code>  
          <run-tests>com.packt:com.packt.lifecycle.sample:run-tests-goal</run-tests>  
          <notify>com.packt:com.packt.lifecycle.sample:notify-goal</notify> 
        </default-phases> 
      </configuration> 
    </component> 
  </components> 
</component-set>

2. 定义Maven plain Old Java Object (MOJO),它用来定义插件的goal,每个类只能处理一个goal。它继承org.apache.maven.plugin.AbstractMojo ,代码如下:

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
/**
* @goal get-code-goal
* @requiresProject false
*/
public class GetCodeGoalMojo extends AbstractMojo {
	public void execute() throws MojoExecutionException,
			MojoFailureException {
		System.out.println("get-code-goal");
	}
}

  注意第一个注解 @goal定义goal的名称,和xml配置中插件get-code冒号后面一致。这里没有具体逻辑,仅是打印一句话,另外的三个类似的类省略了。

3. 定义pom文件
为了接近真实生产情况,我们分成了两个pom。第一个通过mvn clean install 生成插件jar。

<!-- 插件pom文件 -->
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>com.packt.lifecycle.sample</artifactId>  
  <version>1.0.0</version>
  <!-- 这个maven-plugin是固定的,告知maven把该项目打包成插件 -->
  <packaging>maven-plugin</packaging>  
  <dependencies> 
    <dependency> 
      <groupId>org.apache.maven</groupId>  
      <artifactId>maven-plugin-api</artifactId>  
      <version>2.0</version> 
    </dependency> 
  </dependencies> 
</project>

这个执行后,本地仓库如下图:
install后的本地目录

<!-- 要使用packt_lifecycle生命周期打包的项目pom文件 -->
<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>
  <artifactId>com.packt.lifecycle.sample.project</artifactId>  
  <version>1.0.0</version>  
  <packaging>jar</packaging>  
  <name>Custom Lifecycle Project</name>  
  <build> 
    <plugins> 
      <plugin> 
        <groupId>com.packt</groupId>  
        <artifactId>com.packt.lifecycle.sample</artifactId>  
        <version>1.0.0</version>  
        <extensions>true</extensions> 
      </plugin> 
    </plugins> 
  </build> 
</project>

4. 执行上面的pom文件,注意需要先生成插件jar。
运行以下命令可以看到生命周期各阶段逐个执行。注意语法中的notify来源于自定义生命周期的最后一个阶段phase名称。

$ mvn notify

[INFO] Scanning for projects...
[INFO]
[INFO] ------------< com.packt:com.packt.lifecycle.sample.project >------------
[INFO] Building Custom Lifecycle Project 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- com.packt.lifecycle.sample:1.0.0:get-code-goal (default-get-code-goal) @ com.packt.lifecycle.sample.project ---
get-code-goal
[INFO]
[INFO] --- com.packt.lifecycle.sample:1.0.0:build-code-goal (default-build-code-goal) @ com.packt.lifecycle.sample.project ---
build-code-goal
[INFO]
[INFO] --- com.packt.lifecycle.sample:1.0.0:run-tests-goal (default-run-tests-goal) @ com.packt.lifecycle.sample.project ---
run-tests-goal
[INFO]
[INFO] --- com.packt.lifecycle.sample:1.0.0:notify-goal (default-notify-goal) @ com.packt.lifecycle.sample.project ---
notify-goal
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.160 s
[INFO] Finished at: 2020-04-12T09:30:11+08:00
[INFO] ------------------------------------------------------------------------

当然你也可以执行 mvn clean package。
本小讲的代码源于: 代码
为了方便,可以下载我传到CSDN的压缩包



生命周期扩展

  之前的个性化内容是构建内部的事情,那么我们还可以在构建之前和之后做一些事情,有点像切面处理。通过继承org.apache.maven.AbstractMavenLifecycleParticipant 类,重写3个中的任意方法。下面是对方法的说明:

  • afterProjectsRead(MavenSession session): 当所有的MavenProject实例化(就是从pom文件转化的对象)之后被调用。
  • afterSessionEnd(MavenSession session): 当所有工程构建后调用。比如用来清理构建过程中产生的资源文件。
  • afterSessionStart(MavenSession session): 当MavenSession实例化之后,可用来设置执行参数,激活配置等。

以下举例说明:

package com.packt.lifecycle.ext;
import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.execution.MavenSession;
import org.codehaus.plexus.component.annotations.Component;
@Component(role = AbstractMavenLifecycleParticipant.class, hint
="packt")
public class PACKTLifeCycleExtension extends
		AbstractMavenLifecycleParticipant {
	@Override
	public void afterProjectsRead(MavenSession session) {
		System.out.println("All MavenProject instances are created.");
		System.out.println("Offline building: " + session.isOffline());
	}
	@Override
	public void afterSessionEnd(MavenSession session) throws
			MavenExecutionException {
		System.out.println("All Maven projects are built.");
	}
}

该代码所在项目的pom文件,注意package是jar,与之前的例子生成插件不同。

<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>com.packt.lifecycle.ext</artifactId>  
  <version>1.0.0</version>  
  <packaging>jar</packaging>  
  <dependencies> 
    <dependency> 
      <groupId>org.apache.maven</groupId>  
      <artifactId>maven-compat</artifactId>  
      <version>3.2.1</version> 
    </dependency>  
    <dependency> 
      <groupId>org.apache.maven</groupId>  
      <artifactId>maven-core</artifactId>  
      <version>3.2.1</version> 
    </dependency> 
  </dependencies>  
  <build> 
    <plugins>
      <!-- 引入该插件只是为了生成源码的描述文件和类的注释 -->
      <plugin> 
        <groupId>org.codehaus.plexus</groupId>  
        <artifactId>plexus-component-metadata</artifactId>  
        <version>1.5.5</version>  
        <executions> 
          <execution> 
            <goals> 
              <goal>generate-metadata</goal>  
              <goal>generate-test-metadata</goal> 
            </goals> 
          </execution> 
        </executions> 
      </plugin> 
    </plugins> 
  </build> 
</project>

在上面的build成功之后,我们来构建项目:

<project> 
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.packt</groupId>  
  <artifactId>com.packt.lifecycle.ext.sample.project</artifactId>  
  <version>1.0.0</version>  
  <packaging>jar</packaging>  
  <name>Custom Lifecycle Extension Project</name>  
  <build> 
    <extensions> 
      <extension> 
        <groupId>com.packt</groupId>  
        <artifactId>com.packt.lifecycle.ext</artifactId>  
        <version>1.0.0</version> 
      </extension> 
    </extensions> 
  </build> 
</project>

注意和之前的生命周期个性化引用不同,之前的那个是build/plugins,现在的是build/extensions
执行mvn clean install:

[INFO] Scanning for projects...
All Maven project instances are created.
Offline building: false
[INFO]
[INFO] ----------< com.packt:com.packt.lifecycle.ext.sample.project >----------
[INFO] Building Custom Lifecycle Extension Project 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ com.packt.lifecycle.ext.sample.project ---
[INFO] Deleting C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ com.packt.lifecycle.ext.sample.project ---
[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ com.packt.lifecycle.ext.sample.project ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ com.packt.lifecycle.ext.sample.project ---
[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ com.packt.lifecycle.ext.sample.project ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ com.packt.lifecycle.ext.sample.project ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ com.packt.lifecycle.ext.sample.project ---
[WARNING] JAR will be empty - no content was marked for inclusion!
[INFO] Building jar: C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\target\com.packt.lifecycle.ext.sample.project-1.0.0.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ com.packt.lifecycle.ext.sample.project ---
[INFO] Installing C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\target\com.packt.lifecycle.ext.sample.project-1.0.0.jar to R:\CodingRepository\MavenRepository\com\packt\com.packt.lifecycle.ext.sample.project\1.0.0\com.packt.lifecycle.ext.sample.project-1.0.0.jar
[INFO] Installing C:\Users\renzh\Desktop\lifecycle-ext\com.packt.lifecycle.ext.project\pom.xml to R:\CodingRepository\MavenRepository\com\packt\com.packt.lifecycle.ext.sample.project\1.0.0\com.packt.lifecycle.ext.sample.project-1.0.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.785 s
[INFO] Finished at: 2020-04-12T11:29:52+08:00
[INFO] ------------------------------------------------------------------------
All Maven projects are built.

可以看出使用的生命周期是默认的Default。但是开头和结尾执行了我们扩展类的逻辑。

本小讲的代码源于: 代码
为了方便,可以下载我传到CSDN的压缩包,该包对官方的进行了整理,官方的存有歧义。



小结

  本讲介绍了3种生命周期以及它与插件之间的绑定方式,要注意Default生命周期与其他不同,它不与插件直接绑定,而是在特定的打包方式绑定。
  接着介绍如何自定义生命周期以及在(构建)生命周期之前和之后切面处理。

建议先看前面的讲解:
小强说Maven(一)

发布了33 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39722475/article/details/105449094