自己动手插件入门教程Maven(插件仓库项目生命周期目标)「maven 插件原理」

分享自己在Java方面的所思所想,希望你看完之后能有更多更深入的了解本人微信公众号(jwfy的学习分享),欢迎关注~mavenmaven是一个跨平台的项目管理的工具
隶属于Apache下的一个开源项目
主要服务于Java平台的项目构建、依赖管理、项目信息管理等
0、前言在开发java项目的时候,项目编译、测试、运行、打包等等都有着极高的成本,跨部门甚至跨人员之间的项目结构都有可能不一样,除此之外跨部门甚至跨人员之间的项目结构都有可能不一样,这使得成本加倍
maven就是为了解决这些问题的,在跨平台(Linux、Windows、Mac)的基础上,使用Archetype完成各种项目统一的骨架,还进行标准化的构建过程,包含了包版本管理、库依赖、集成测试、编译、安装、运行等
接下来,将分别认识和学习setting.xml、仓库、生命周期、插件等模块
(注:本文所有的路径都是在Mac下面的,其他平台类似)1、setting.xmlsetting.xml 是maven的管理配置文件,包含了系统级别的配置和当前用户级别的配置,用户级别的路径是~/.m2,系统级别的是$M2_HOME/conf,当然了$M2_HOME是在安装的时候设置好的maven路径,我们一般使用的是用户级别的文件
该文件可以配置仓库、代理、profile、镜像、插件等,更多更详细的配置setting.xml<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd\"> <localRepository/> <interactiveMode/> <usePluginRegistry/> <offline/> <pluginGroups/> <servers/> <mirrors/> <proxies/> <profiles/> <activeProfiles/></settings>localRepository本地仓库地址,默认情况下,下载到本地的代码库存放在${user.home}/.m2/repository文件夹中的,用户如果想存在放其他地方,配置该属性即可interactiveMode用户交互模式,默认为trueusePluginRegistry是否使用插件仓库、默认为false、如果需要使用,则需要在当前路径下创建plugin-registry.xml文件并设置为trueoffline是否在离线状态下运行,如果系统需要在离线状态下运行,则设置为true,默认为falsepluginGroups插件组,当插件的groupId没有显式提供时,供搜寻插件(groupId)的列表 <pluginGroups> <pluginGroup>org.mortbay.jetty</pluginGroup> </pluginGroups>入上述代码,包含了org.mortbay.jetty 的插件,例如,当我们需要执行该插件的run目标时,可以执行org.mortbay.jetty:jetty-maven-plugin:run,又或者可以简化成mvn jetty:run,关于为何会简化成如此,到插件的部分细说
servers服务配置,主要是针对需要鉴权的仓库的配置,有些仓库默认匿名用户可以访问,但是可能存在一些私服需要对用户进行鉴权,有相关权限的用户才可以继续访问或者操作
<servers> <server> <id>server001</id> <!-- 服务ID,和仓库ID关联 --> <username>my_login</username> <!-- 用户名 --> <password>my_password</password> <!-- 密码 --> <privateKey>${user.home}/.ssh/id_dsa</privateKey> <!-- 私钥路径 --> <passphrase>some_passphrase</passphrase> <!-- 私钥密码 --> <filePermissions>664</filePermissions> <!-- 文件被创建权限--> <directoryPermissions>775</directoryPermissions> <!-- 文件夹被创建权限 --> <configuration></configuration> </server> </servers>mirrors镜像地址,对仓库地址的一种映射关系,国外的仓库地址可能不是很稳定,类似于AliBaBa和OSchina搭建的镜像地址,我们可以配置镜像地址,使得访问速度更快 <mirrors> <mirror> <id>planetmirror.com</id> <!-- 镜像ID,唯一性即可--> <name>PlanetMirror Australia</name> <!-- 镜像名字 --> <url>http://downloads.planetmirror.com/pub/maven2</url> <!-- 镜像地址 --> <mirrorOf>central</mirrorOf> <!-- 映射的具体仓库ID以及其操作--> </mirror> </mirrors>没有镜像的时候需要直连A仓库,如果A仓库在国外可能存在各种问题不稳定工作有镜像的时候这时候存在一个B镜像,可以间接的访问到国外不稳定的A仓库,那我们就可以去加入B镜像就可以了
需要注意下mirrorOf,这个参数有多种配置,而且是针对仓库ID过滤的 表示所有external: = 所有不在本地、不适用file://协议的repo,repo1 = repo和repo1仓库,!repo1 = 所有的仓库,除了repo1更多细节可看官方文档===>Guide to Mirror Settings-proxies代理,主要是为了便于在各自网络环境下使用<proxies> <proxy> <id>myproxy</id> <!-- 代理ID,唯一性即可--> <active>true</active> <!-- 是否激活,激活了就可以使用该代理--> <protocol>http</protocol> <host>proxy.somewhere.com</host> <port>8080</port> <!-- 协议、主机、端口--> <username>proxyuser</username> <password>somepassword</password> <nonProxyHosts>.google.com|ibiblio.org</nonProxyHosts> <!-- 不代理的主机,中间用竖划线区分--> </proxy></proxies>profiles 和 activeProfilesprofile,拥有多套环境,可以根据参数任意切换,如需使用需要在activeProfiles中加入该profile的ID
包含了activation, repositories, pluginRepositories和properties四种元素,其中activation就类似于properties,就和一个KV对一般,剩下的就是仓库和插件了Repositories仓库的配置,其中包含了快照版本SNAPSHOT和发布版本RELEASE<repositories> <repository> <id>codehausSnapshots</id> <!-- 仓库ID 需唯一 --> <name>Codehaus Snapshots</name> <!-- 仓库名称 --> <releases> <!-- 正式版本一--> <enabled>false</enabled> <!-- 是否可正常使用 --> <updatePolicy>always</updatePolicy> <!-- 更新策略 --> <checksumPolicy>warn</checksumPolicy> <!-- 校验策略,分为ignore、fail、warn --> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> <url>http://snapshots.maven.codehaus.org/maven2</url> <!-- 仓库地址,按protocol://hostname/path形式 --> <layout>default</layout> <!-- --> </repository></repositories>如上代码,需要注意 updatePolicy 更新策略,主要是如下几种值always 总是更新daily 每天更新一次(默认值)interval:X X分钟更新一次never 永远都不更新2、仓库什么叫仓库呢?顾名思义就是存放物品的地方,在Java项目中肯定就是存放了各种各样的jar包,避免了每开发一个项目都要导入各种各样的jar包,全部统一存放在仓库中,使用的时候只需要在POM中声明所需要的包的GroupId和artifactId,在使用的时候会自动关联相关jar包
如下图是仓库的分类图刚刚安装maven之后,是没有本地仓库的,必须得有一个可用的远程仓库,这样才可以把需要的构建下载到本地来的私服是一种特殊的远程仓库,搭建在局域网内的仓库,私服代理广域网的仓库,提供给局域网内的用户使用,可用减少局域网内的用户同外界仓库的传输,每一个jar包只需要拉取一次就可以提供给局域网内所有的用户使用,并且也更加稳定减少网络带宽流量加速Maven构建部署第三方构件提高稳定性、增强控制降低中央仓库的负载部署到远程仓库上面所说的仓库和我们现在说的远程仓库是同一种东西,只是一个是推送到远程,另一个是拉取到本地使用
部署到远程仓库,需要在各自项目的POM文件中部署好distributionManagement元素,例如如下代码<distributionManagement> <repository> <id>deployment</id> <name>internal repository for releases</name> <url>http://test.com/nexus/releases/</url> </repository> <snapshotRepository> <id>deployment</id> <name>internal repository for snapshots</name> <url>http://test.com/nexus/snapshots/</url> </snapshotRepository></distributionManagement>配置中包含了release和snapshot两种版本模式,前者表示稳定版本,一般情况下提供给别人活着发布到生产环境的都是该版本,snapshot快照版本则是处于开发模式
而且如果当前项目是快照版本则部署到快照仓库,否则就部署到发布仓库
在部署到远程仓库需要用户认证的时候,就需要设置在setting.xml的service元素了,在service中的ID必须和仓库的ID保持一致
部署是通过maven-deploy-plugin的deploy功能完成部署操作的,插件到后面细说
3、坐标和依赖maven的坐标预定了世界上任何一个构建的位置,只需要约定好正确的坐标,就可以正确的找到所需的构建,坐标元素包括groupId,artifactId, version等
坐标详解groupId (必选)定义当前maven项目隶属的实际项目artifactId (必选)实际项目中的一个maven模块version (必选)版本号packaging (可选)打包方式,常用的分为jar和war包,插件则是maven-plugin,默认为jar包classifier (不可直接定义)帮助定义构建出的一些附属构建,例如source和对应的doc依赖配置依赖配置是在pom文件中的配置的元素,在dependencies中的,可以包含一个或者多个dependency元素,每个依赖包含如下元素groupId、artifactId、version 依赖的基础坐标,最重要的type 依赖的类型,对应到坐标的packaging,默认情况下不必声明,其值为jarscope 依赖的范围,不同的依赖会使用不同的classpathcompile 编译依赖的范围,没有指定则默认使用该范围test 测试,只对测试的classpath有效provided 已提供依赖范围,在编译和测试时有效,但是在运行时无效runtime 运行时的依赖范围system 系统依赖范围,和本机系统绑定import 导入依赖范围optional 依赖是否为可选,确认依赖是否被继承exclusions 排除传递性依赖依赖调解传递性依赖机制能够大大的简化依赖声明,而且大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖,但是当出现冲突了,则需要很清楚依赖性依赖是从什么依赖路径引入的
例如A项目有这样的依赖关系A->B->C->X(1.0),A->B->X(2.0),那么哪个X版本会被引用呢?第一原则:最短路径优先,上述例子中会使用X2.0版本,但是这并不能解决所有的问题,例如又有如下的依赖关系A->B->D(1.0),A->B->D(2.0),使用最短路径则出现的了不确定性第二原则:第一声明者优先,这个例子中,如果D1.0版本在pom中更加靠前,则会使用D1.0版本可选依赖确认依赖是否被继承,如下图所示,如果B有optional元素的依赖则表示该元素是可选依赖,从A出发的依赖传递性就被打破了,A无法依赖X,Y了,一般情况下,也不怎么使用该情况依赖查看依赖可以通过dependency插件查看更多信息,例如mvn dependency:tree查看最后的依赖结果dependency:list 展示所有的依赖情况dependency:tree 树形结构展示依赖情况dependency:analyze 分析依赖树4、生命周期和插件软件开发的整个过程也是有生命周期的,开发、编译、测试以及部署,而maven的生命周期就是为了所有对象构建过程进行的抽象和统一
maven的生命周期包含了项目的清理、初始化、编译、测试、打包以及集成测试,和最后的验证和部署操作
maven的生命周期本身是抽象的,不参与任何具体的工作,只是调用各种插件完成所需的任务
例如常见的插件有maven-compiler-plugin 编译使用的插件maven-surefire-plugin 测试使用的插件maven-deploy-plugin 部署使用的插件生命周期maven包含三大类相互独立的生命周期,具体的一个生命周期包含了多个阶段,在使用如下操作的时候,背后都会映射到各个插件的目标上包含的三大类生命周期是- clean 生命周期pre-clean 清理项目前的准备工作clean 清理之前构建所生成的文件post-clean 执行一些清理完之后的事情- default 生命周期validateinitializegenerate-sourcesprocess-sourcesgenerate-resourcesprocess-resourcescompile 编译项目的代码输出到clasapath文件夹process-classesgenerate-test-sourcesprocess-test-sourcesgenerate-test-resourcesprocess-test-resourcestest-compile 编译项目的测试代码,输出到测试的classpath文件夹process-test-classestest 使用测试单元框架运行测试代码prepare-packagepackagepre-integration-testintegration-testpost-integration-testverifyinstall 安装到本地仓库deploy 部署到远程仓库- site 生命周期pre-site 执行生成项目站点文件前处理一些事情site 生成项目站点文件post-site 执行生成项目站点文件后处理一些事情site-deploy 部署站点文件到服务器再次强调,生命周期本身是抽象的,具体的功能执行是有插件完成的,那么自然而然,插件肯定需要和生命周期以及生命周期的阶段进行绑定操作常用命令行操作mvn clean 真正执行的是clean的pre和clean阶段mvn test 真正执行的是default中直到test阶段的全过程mvn clean install 真正执行的是clean直到clean阶段以及default的到install阶段的全过程mvn clean deploy 真正执行的是clean直到clean阶段以及default的deploy阶段全过程在进行操作的时候,如果出现了版本冲突等情况,可以加上参数 \"-U\" 或者 \"--update-snapshots\",其意思是Forces a check for missing releases and updated snapshots on remote repositories插件绑定上文已经说了,生命周期最后执行任务是由插件完成的,而且插件的目标goal和生命周期的阶段phase是绑定的,接下来就介绍下这两者的关系
插件是以独立的构建完成的,并不存放在maven的核心包中,在使用的时候下载到本地即可,而且为了能够复用代码,能够完成多个任务,因此一个插件存放了多个功能,每一个功能都是一个目标,想查看具体插件的具体目标,可使用mvn help:description -Dplugin=groupId:archetypeId:version -Ddetail,查看插件的详情内容,其中包含了插件目标,而且包含到了绑定到生命周期的阶段
这个就是maven-help-description 插件,例如mvn help:description -Dplugin=org.apache.maven.plugins:maven-compile-plugin:3.1 -Ddetail还可继续简化为mvn help:description -Dplugin=compile -Ddetailorg.apache.maven 是属于系统默认的,使用插件的目标前缀compile就可以找到所需的具体插件了其中detail参数是获取该插件的详细信息插件目标前缀mvn clean 其实就是使用了插件前缀,具体本地可查看~/.m2/repository/org/apache/maven/plugins/maven-metadata-central.xml内容如图所示,prefix元素内容是clean,指定的插件则是maven-clean-plugin,这样maven就知道是真正执行哪个具体的插件(明确了gruopId和artifactId)插件配置插件配置主要是给插件传递运行参数和修改插件映射到生命周期的阶段,主要有如下操作命令行配置例如在编译、安装的时候需要跳过测试,则可以使用 mvn install -Dmaven.test.skil=true 其中-D参数是java自带的,此功能是通过命令行设置一个java属性POM全局配置配置在项目的pom文件中的,整个项目都是通用的插件,例如下面的配置,就是给整个项目配置的java版本,以及跳过执行测试功能<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>${java_source_version}</source><target>${java_target_version}</target><encoding>${file_encoding}</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><skipTests>true</skipTests></configuration></plugin></plugins>POM任务配置和全局配置相对应,是对某个具体插件的参数设置,可以设置参数,也可以重新绑定生命周期的阶段phase5、聚合和继承此小节主要是涉及到多模块下的各个POM文件的关系
一个项目中如果存在多个模块,就会使得一个父POM挂着多个子POM
如果实际开发中,只是单独开发子模块,并不关心父模块,所以在引用父模块的时候需要指定父POM的路径,使用relativePath关键字,如下图所示因为存在父亲模块,所以子模块只需要继承父POM引入的各种jar包,无需再次引入,类似于springboot、junit等模块,存在父POM即可
不过,同时又有另一种情况的存在,例如新加入的子模块,无需引入上述模块,应该如何处理呢?POM中的dependencyManagement元素既可以让子模块继承父模块的配置,也可以保证子模块的灵活
父亲模块<properties> <spring.version>4.3.9.RELEASE</spring.version></properties><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> </dependencies></dependencyManagement>子模块<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency></dependencies>6、插件编写代码模板编写自定义的插件,除了引入特定的jar包之外还有些其他的设定,接下来就介绍个插件开发的过程引入API插件包<dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> <version>3.3.9</version></dependency>项目package<packaging>maven-plugin</packaging>Mojo文件public class HelloMojo extends AbstractMojo { public void execute() throws MojoExecutionException, MojoFailureException { ... }}Mojo文件需要继承AbstractMojo,并实现execute方法Mojo标注、参数标注Mojo标注是放在类的层次上的,主要包含如下参数@goal <goalName> 唯一必需的注解,它给予目标一个在插件中唯一的名称
也就是所谓的插件目标@phase <phaseName> 指定目标默认的生命周期阶段
如果你将该目标的执行配置到了pom.xml而且没有指定一个阶段
Maven就会使用该注解的值将其绑定到一个默认的阶段
@requiresDependencyResolution <requireScope> 标记该mojo在可以运行之前,需要特定范围(或者一个暗指的范围)的依赖
支持的范围有compile,runtime,和test
如果该注解有一个值为test,那么就是告诉Maven,除非测试范围的所有依赖都被正确解析了,否则该mojo不能运行
@requiresProject (true|false) 标记该mojo必须在一个项目中运行,默认为true
这一点插件类型和骨架类型(archetype)相反,后者默认为false
@requiresReports (true|false) 如果你正创建一个依赖于报告的项目,你就需要将requiresReports设置成true
该注解默认的值是false
@aggregator (true|false) 一个aggregator设置成true的Mojo在Maven运行的时候只会被执行一次,有了该选项,插件开发者就可以汇总一系列构建的输出;例如,创建一个插件用来汇总一次构建包含的所有项目的报告
aggregator设置成true的目标只针对Maven构建的顶层项目运行
该注解默认的值是false
@requiresOnline (true|false) 当该注解的值是true的时候,Maven在脱机模式运行的时候该目标运行就会失败
Maven会抛出一个错误
默认值:false
@requiresDirectInvocation 当设置成true的时候,只有当用户显式的从命令行触发的时候,该插件才能得以执行
如果有人试图将其绑定到一个生命周期阶段,Maven就会抛出一个错误
默认值是false
@execute goal=<goalName> 运行该目标之前先执行另一个目标@execute phase=<phaseName> 运行该目标之前先执行另一个生命周期的目标@execute lifecycle=<lifecycle> phase=<phaseName> 运行该目标之前先执行自定义的生命周期到特定的阶段参数主要是设置插件运行时需要的各种参数,支持各种基础数据类型还包含list、map、URL、file、Date等,如/ rootpath @parameter expression=\"${project.basedir}\"/File rootPath;7、参考链接http://book.huihoo.com/maven-the-definitive-guide/index.htmlmaven 实战原创推荐「系列教程」手写RPC框架(1),看看100个线程同时调用情况如何「面试」new String(\"abc\")和\"abc\"有什么区别?反编译看看原理吧「源码」SpringBoot启动原理,你是否清楚?「深入」HashMap再学习,相比Java7,Java8源码你了解多少「面试」LRU了解么?看看LinkedHashMap如何实现LRU算法
自己动手插件入门教程Maven(插件仓库项目生命周期目标)
(图片来源网络,侵删)

联系我们

在线咨询:点击这里给我发消息