• maven系列(五)生命周期和插件


    除了坐标、依赖以及仓库以外,maven另外两个核心概念就是生命周期和插件。maven的生命周期是抽象的,其实际行为都是由插件来完成。

    7.1 什么是生命周期

    在maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理、编译、测试以及部署。

    Maven的生命周期就是为了对所有的构建过程进行抽象和统一。Maven从大量项目和构建工具中学习和反思,然后总结出一套高度完善的、易扩展的生命周期。这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。也就是说,几乎所有项目的创建,都能映射到这样一个生命周期上。

    Maven的生命周期是抽象的, 这意味着生命周期本身不做任何实际任务,在maven的设计中,实际的任务都交给插件来完成。这种思想与设计模式的模板方法(template method)非常相似。模板方法在父类中定义算法的整体结构,子类可以通过实现或者重写父类的方法来控制实际的行为,这样既保证了算法有足够的可扩展性,又能够严格控制算法的整体结构。如下的模板方法抽象类能够很好的体现maven生命周期的概念。

    public abstract class AbstractBuild {
        private AbstractBuild(){}
        public void build() {
            init();
            compile();
            test();
            packagee();
            integrationTest();
            deploy();
        }
    
        /**
         * 抽象方法 初始化
         */
        protected abstract void init();
    
        /**
         * 抽象方法 编译
         */
        protected abstract void compile();
    
        /**
         * 测试
         */
        protected abstract void test();
    
        /**
         * 打包:因为package是关键词
         */
        protected abstract void packagee();
    
        /**
         *  集成测试
         */
        protected abstract void integrationTest();
    
        /**
         * 部署
         */
        protected abstract void deploy();
    }
    

    这个类没有具体实现初始化,编译,测试等行为,它们都交由子类去实现。

    虽然上述代码和Maven实际代码相去甚远,Maven的生命周期包含更多的步骤和更复杂的逻辑,但是它们的基本理念是相同的。为了不让用户重复发明轮子,Maven设计了插件机制。

    每个构建步骤都可以绑定一个或多个插件行为,而且maven为大多数构建步骤编写并绑定了默认插件。例如,针对编译的插件有maven-compiler-plugin,针对测试的插件有maven-surefire-plugin等。当用户有特殊需要的时候,也可以配置插件定制构建行为,甚至自己编写插件。

    maven定义的生命周期和插件机制一方面保证了所以Maven项目有一致的构建标准,另一方面又通过默认插件简化和稳定了实际项目的构建。此外,扩展性也不错。

    7.2 生命周期详解

    三套生命周期

    Maven其实拥有三套相互独立的生命周期,他们分别为clean、default和site。clean生命周期的目的是清理项目,default生命周期的目的是构建项目,而site则是建立项目站点。

    每个生命周期包含一些阶段(phase),这些阶段是有顺序的,且具有依赖的关系。

    但是三套生命周期本身是相互独立的, 用户可以仅仅调用default生命周期的某个阶段,而不会对其他生命周期产生任何影响。

    clean生命周期

    clean生命周期的目的是清理项目,它包含三个阶段:

    1. pre-clean:执行一些清理前需要完成的任务
    2. clean:清理上一次构建生成的文件
    3. post-clean:执行一些清理后需要完成的工作

    default生命周期

    default生命周期定义了真正构建时所需要的所有步骤,它是所有生命周期中最核心的部分。

    • validate
    • initialize
    • generate-sources:
    • process-sources:处理项目主资源文件。一般来说,是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中。
    • generate-resources:
    • process-resources:
    • compile:编译项目的主源码。一般来说,是编译src/main/java目录下的Java文件至项目输出的主classpath目录中。
    • process-classes
    • generate-test-sources
    • process-test-sources处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作以后,复制到项目输出的测试classpath目录中。
    • generate-test-resources:
    • process-test-resources:
    • test-compile:编译项目的测试代码。一般来说,是编译src/test/java目录下的java文件至项目输出的测试classpath目录中。
    • process-test-classes
    • test:使用单元测试框架运行测试,测试代码不会被打包或部署。
    • prepare-package
    • package:接受编译好的代码,打包成可发布的格式,如JAR。
    • pre-integration-test:
    • integration-test
    • post-integration-test:
    • verify:
    • install:将包安装到maven本地仓库,供本地其他maven项目使用。
    • deploy:将最终的包复制到远程仓库,供其他开发人员和maven项目使用。

    site生命周期

    site生命周期的目的是建立和发布项目站点,maven能够基于pom所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息。该生命周期包括:

    • pre-site:执行一些在生成项目站点之后需要完成的工作。
    • site:生成项目站点文档。
    • post-site:执行一些在生成项目站点之后需要完成的工作。
    • site-deploy:将生成的项目站点发布到服务器上。

    命令行与生命周期

    以常见命令解释其执行的生命周期阶段:

    • $mvn clean:该命令调用clean生命周期的clean阶段。实际执行的阶段为clean生命周期的pre-cleanclean阶段。
    • $mvn test:该命令调用default生命周期的test阶段。实际执行的阶段为default生命周期的validateinitialize等,直到test的所有阶段。这也解释了为什么在执行测试的时候,项目的代码能够自动得以编译。
    • $mvn clean install:该命令调用clean生命周期的clean阶段和default生命周期的install阶段。实际执行的阶段为clean生命周期的pre-cleanclean阶段,以及default生命周期从validate到install的全部阶段。该命令结合了两个生命周期,在执行正在的项目构建之前清理项目是一个很好的实践。

    7.3 插件目标

    Maven的核心仅仅定义了抽象的生命周期,具体的任务是交给插件完成的,插件以独立的构件形式存在,因此,maven核心的分发包只有3MB,Maven会在需要的时候下载并使用插件。

    maven-dependency-plugin有十多个目标,每个目标对应一个功能,上述提到的几个功能分别对应dependency:analyzedependency:treedependency:list

    冒汗前面是插件前缀,冒号后面是该插件的目标。

    类似的还有:surefire:test这个是maven-surefile-plugin的test目标。

    7.4 插件绑定

    Maven的生命周期与插件相互绑定,用以完成实际的构建任务。具体而言,是生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务。

    内置绑定

    为了能让用户几乎不用任何配置就能构建Maven项目,Maven在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应的插件目标就会执行相应的任务。

    clean生命周期仅有的pre-cleancleanpost-clean三个阶段,其中的cleanmaven-clean-plugin:clean绑定。

    自定义绑定(重点)

    除了内置绑定以外,用户还能够自己选择将某个插件绑定到生命周期的某个阶段,这种自定义绑定方法能让maven项目在构建过程中执行更多任务。

    一个常见的例子是创建项目的源码jar包,内置的插件绑定关系中并没有涉及这一任务,因此需要用户自行配置。maven-source-plugin可以帮助我们完成该任务,它的jar-no-fork目标能够将项目的主代码打包成jar文件。

    <build>
    	<plugins>
        	<plugin>
            	<GAV/>
                <executions>
                	<execution>
                    	<id>task1</id>
                        <phase>varify</phase>
                        <goals>
                        	<goal/>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    
    </build>
    

    在POM的build元素下的<plugins>字元素中声明插件的使用。对于自定义绑定的插件,用户总是应该声明一个非快照版本,这样可以避免由于版本变化造成的构建不稳定性。

    ​ 在上述配置中,除了基本的插件坐标声明外,还有插件执行配置,<executions>下每个execution资源时可以用来配置执行一个任务。该例子中配置了一个id为task1的任务,通过phrase配置,将其绑定到verify声明周期阶段上,再通过goals配置指定要执行的插件目标。至此,自定义插件绑定完成。运行mvn verify就能看到如下输出。

    我们知道,当插件目标被绑定到不同的生命周期阶段的时候,其执行顺序会由生命周期阶段的先后顺序决定。如果多个目标被绑定到同一个阶段,这些插件声明的先后顺序决定了目标的执行顺序。

    7.5 插件配置

    完成了插件和生命周期的绑定之后,用户还可以配置插件目标的参数,进一步调整插件目标所执行的任务,以满足项目的需求。

    命令行插件配置

    用户可以在maven命令中使用-D参数,并伴随一个参数键=参数值的形式来配置插件目标的参数。

    比如在maven-surefile-plugin中提供了maven.test.skip参数:

    mvn install -Dmaven.test.skip=true
    

    -D是用来在启动一个java程序时设置系统属性值的。如果该值是一个字符串且包含空格,那么需要包在一对双引号中。

    POM中插件全局配置

    用户可以在声明插件的时候,对此插件进行一个全局的配置。例如我们需要配置maven-compiler-plugin告诉它编译Java 1.5版本的源文件,生成与JVM1.5兼容的字节码:

    <build>
    	<plugins>
       		<plugin>
            	<groupId>org.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                	<source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    这样,不管绑定到compile阶段的maven-compiler-plugin:compile任务,还是绑定到test-compiler阶段的maven-compiler-plugin:testCompiler任务,就都能够使用该配置,基于1.5版本进行编译。

    POM中插件任务配置

    7.6 获取插件信息

    在线插件信息

    基本上所有主要的maven插件都来自Apache和codehaus

    网址:https://maven.apache.org/plugins/index.html

    托管于Codehaus上的Mojo项目也提供了大量maven插件。但是该网址已经关停了

    使用maven-help-plugin描述插件

    mvn help:describe -Dplugin = org.apache.maven.plugins:maven-compiler-plugin:2.1
    

    执行了maven-help-plugindescribe目标。在参数plugin中输入需要描述插件的GAV。

    【略】

    常见插件

    插件名称 用于 来源
    maven-clean-plugin
    maven-compiler-plugin
    deploy
    install
    resources 处理资源文件
    maven-site-plugin 生成站点
    maven-surefire-plugin 执行测试
    maven-jar-plugin 构建jar
    maven-javadoc-plugin 生成javadoc文档
    maven-pmd-plugin 生成PMD报告
    maven-assembly-plugin 构建自定义格式的分发包
    maven-enforcer-plugin 定义规则并强制要求项目遵循
    maven-source-plugin 生成源码包
    properties-maven-plugin 从properties文件读写maven属性
    jetty-maven-plugin 集成Jetty容器,实现快速开发测试

    7.8 插件解析机制

    为了方便用户使用和配置插件,maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件,maven的这一特性是一把双刃剑,虽然它简化了插件的使用和配置,可一旦插件的行为出现异常,用户就很难快速定位到出现问题的构建。比如

    mvn help:system
    

    这一条命令,它到底执行了什么插件?下面介绍原理。

    插件仓库

    与依赖构件一样,插件构件同样基于坐标存储在maven仓库中。在需要的时候,maven会从本地查找,如果没有去远程操作找。

    但是maven会区别对待依赖的远程仓库和插件的远程仓库。当maven需要的依赖在本地仓库不存在时,它会去所配置的远程仓库查找,可是当maven需要的插件在本地仓库不存在时,它就不会去这些远程仓库查找。

    插件的远程仓库使用pluginRepositories和pluginRepository配置。

    插件的默认groupId

    在POM中配置插件的时候,如果该插件是maven的官方插件,就可以忽略groupId配置。

    <build>
    	<plugins>
        	<plugin>
            	<artifactId>maven-compiler-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                	<source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    上述配置就省略了groupId,maven在解析该插件的时候,会自动用默认groupId:org.apache.maven.plugins补齐。【不推荐】

    解析插件版本

    同样是为了简化插件的配置和使用,在用户没有提供版本的情况下,maven会自动解析插件版本。

    首先,maven在超级POM中为所有核心插件设定了版本,超级POM是所有maven项目的父POM。所有项目都继承这个超级POM配置。因此,即使用户不加任何配置,maven使用核心插件的时候,它们的版本就已经确定了。

    如果用户使用某个插件时,没有设定版本,而这个插件又不属于核心插件的范畴,maven就会去检查所有仓库中可用的版本,然后做出选择。

    maven遍历本地仓库和所有远程插件仓库,将该路径下的仓库元数据归并后,就能计算出latest和release的值。然后使用release而不是latest,避免由于快照频繁更新导而导致的插件行为不稳定。

    【但是不设定版本是不推荐的, 同样超级POM中为核心插件已经设定了版本】

    解析插件前缀

    插件前缀与groupId:artifactoryId是一一对应的,这种匹配关系存储在仓库元数据中。与依赖的groupId/artifactoryId/maven-metadata.xml不同,这里的仓库元数据为groupId/maven-metadata.xml

    Maven在解析插件仓库元数据的时候,会默认使用org.apache.maven.pluginsorg.codehaus.mojo两个groupId。

    也可以通过配置settings.xml让maven检查其他groupId上的插件仓库元数据:

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

    基于该配置,maven就不仅仅会检查这两个地方的xml了。还会检查com/ssozh/plugins/maven-metadata.xml

    <metadata>
    	<plugins>
        	<plugin>
            	<name>Maven Dependency Plugin</name>
                <prefix>dependency</prefix>
                <artifactId>mavne-dependency-plugin</artifactId>
            </plugin>
        </plugins>
    </metadata>
    

    上述内容中可以看出,当Maven解析到dependency:tree这样的命令的时候,它首先基于默认的groupId归并所有插件仓库的元数据,其次检查归并后的元数据,找到对应的artifactId。然后结合当前元数据的groupId。最后使用上述方法获取version。这时就得到了完整的插件坐标。

    如果该maven-metadata.xml没有记录该插件前缀,就去mojo然后去自定义,如果都没有,则报错。

    7.9 小结

    本章介绍了maven的生命周期和插件这两个重要的概念。不仅解释了生命周期背后的理念,还详细阐述了clean、default、site三套生命周期各自的内容。此外,本章还重点介绍了maven插件如何与生命周期绑定,以及如何配置插件行为,如何获取插件信息。

    补充

    generate-source:

  • 相关阅读:
    gsoap 学习 1-自己定义接口生成头文件
    arcgis for silverlight 鼠标点击地图获取当前经纬度
    远程桌面服务器和本机粘贴板共享
    开源ORM
    Memcached
    Visual Studio一些插件
    asp.net 中的事务
    (转载)轻量级表达式树解析框架Faller
    冒泡排序
    (转)理解POCO
  • 原文地址:https://www.cnblogs.com/SsoZhNO-1/p/15516525.html
Copyright © 2020-2023  润新知