关于Maven,这些应该够用了
一、Maven的作用
目的是一个可以用于构建和管理任何基于Java的项目的工具,想要一种标准的方式来构建项目,清晰地定义项目的组成,一种简单的方式来发布项目信息,以及一种在多个项目中共享JAR的方式。
Maven是基于项目对象模型(POM project object model)来构建项目,用通俗点的话说就是对要构建的项目进行建模,将要构建的项目看成是一个对象(Object),用PO来指代这个对象,pom.xml就是PO对象的XML描述。
二、Maven安装配置
安装后可以通过Maven的命令使用相关的功能。
和配置JDK类似,需要配置环境变量。
三、安装目录
以windows操作系统安装目录为例
--bin 执行脚本所在的目录,如mvn
--boot 里面就一个jar包:plexus-classworlds-2.5.2.jar
,plexus-classworlds
是一个类加载器框架,相对于默认的 java 类加载器,它提供了更丰富的语法以方便配置,Maven使用该框架加载自己的类库。
--conf 包含settings.xml
文件,可以全局定制maven行为
--lib 该目录包含了maven运行时需要的java类库
四、超级POM
在安装目录的lib
包下面有个maven-model-builder-3.2.5.jar
包, 用解压工具打开org
->apache
->maven
->model
,会在其目录下面发现一个pom-4.0.0.xml
。
内容如下:
<?xml version="1.0" encoding="UTF-8"?><!-- START SNIPPET: superpom -->
<project><modelVersion>4.0.0</modelVersion><!-- 配置默认的远程仓库,即中央仓库 --><repositories><repository><id>central</id><name>Central Repository</name><url>https://repo.maven.apache.org/maven2</url><layout>default</layout><snapshots><enabled>false</enabled></snapshots></repository></repositories><!-- 配置默认的插件远程仓库,也是用的中央仓库 --><pluginRepositories><pluginRepository><id>central</id><name>Central Repository</name><url>https://repo.maven.apache.org/maven2</url><layout>default</layout><snapshots><enabled>false</enabled></snapshots><releases><updatePolicy>never</updatePolicy></releases></pluginRepository></pluginRepositories><!-- 配工程构建信息 --><build><directory>${project.basedir}/target</directory><outputDirectory>${project.build.directory}/classes</outputDirectory><finalName>${project.artifactId}-${project.version}</finalName><testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory><sourceDirectory>${project.basedir}/src/main/java</sourceDirectory><scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory><testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory><resources><resource><directory>${project.basedir}/src/main/resources</directory></resource></resources><testResources><testResource><directory>${project.basedir}/src/test/resources</directory></testResource></testResources><pluginManagement><!-- NOTE: These plugins will be removed from future versions of the super POM --><!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) --><plugins><plugin><artifactId>maven-antrun-plugin</artifactId><version>1.3</version></plugin><plugin><artifactId>maven-assembly-plugin</artifactId><version>2.2-beta-5</version></plugin><plugin><artifactId>maven-dependency-plugin</artifactId><version>2.8</version></plugin><plugin><artifactId>maven-release-plugin</artifactId><version>2.3.2</version></plugin></plugins></pluginManagement></build><reporting><outputDirectory>${project.build.directory}/site</outputDirectory></reporting><profiles><!-- NOTE: The release profile will be removed from future versions of the super POM --><profile><id>release-profile</id><activation><property><name>performRelease</name><value>true</value></property></activation><build><plugins><plugin><inherited>true</inherited><artifactId>maven-source-plugin</artifactId><executions><execution><id>attach-sources</id><goals><goal>jar</goal></goals></execution></executions></plugin><plugin><inherited>true</inherited><artifactId>maven-javadoc-plugin</artifactId><executions><execution><id>attach-javadocs</id><goals><goal>jar</goal></goals></execution></executions></plugin><plugin><inherited>true</inherited><artifactId>maven-deploy-plugin</artifactId><configuration><updateReleaseInfo>true</updateReleaseInfo></configuration></plugin></plugins></build></profile></profiles></project>
<!-- END SNIPPET: superpom -->
超级POM是所有maven项目的父pom,所有项目都继承这个超级pom。子pom.xml会完全继承父pom.xml中所有的元素,而且对于相同的元素,一般子pom.xml中的会覆盖父pom.xml中的元素,但是有特殊的元素它们会进行合并而不是覆盖;如plugin插件。
五、setting.xml配置文件
settings.xml中包含类似本地仓储位置、修改远程仓储服务器、认证信息等配置。
settings.xml文件一般存在于两个位置:
全局配置: ${M2_HOME}/conf/settings.xml
用户配置: user.home/.m2/settings.xml
note:用户配置优先于全局配置。
示例:
<?xml version="1.0" encoding="UTF-8"?><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 http://maven.apache.org/xsd/settings-1.0.0.xsd"><!-- 配置本地仓库位置 --><localRepository>D:/.m2/repository</localRepository><servers><!--服务器认证信息--><server><id>epoint-nexus</id><username>epointyanfa</username><password>epoint_yanfa</password></server></servers> <mirrors>
<!-- 配置镜像 --><mirror><id>epoint-nexus</id><name>Epoint Nexus</name><url>http://192.168.0.99:8081/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf></mirror></mirrors><profiles><!-- 配置私服 --><profile><id>epoint-nexus</id><repositories><repository><id>epoint-nexus</id><name>Epoint Nexus Repository</name><url>http://192.168.0.99:8081/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories><pluginRepositories><pluginRepository><id>epoint-nexus</id><name>Epoint Nexus Repository</name><url>http://192.168.0.99:8081/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></pluginRepository></pluginRepositories></profile></profiles><!-- 激活私服 --><activeProfiles><activeProfile>epoint-nexus</activeProfile></activeProfiles>
</settings>
弄清上面配置之前先要搞懂几个概念:仓库、镜像。
六、Maven仓库和镜像
6.1、仓库
主要分为两种:本地仓库和远程仓库,maven的规则是优先在本地仓库进行寻找,如果本地仓库没有,那么便从远程仓库进行获取并下载到本地仓库。如果二者都没有,那么会报错。
本地仓库:本地仓库是本地的缓存副本,主要起缓存作用。在maven安装目录的conf下有个settings.xml的文件,可以对本地仓库的路径进行设置
<localRepository>D:/.m2/repository</localRepository>
远程仓库:私服和中央仓库都属于远程仓库。
私服:如公司搭建的基于内网可访问的maven服务器;主要是指局域网内的maven服务器。
6.2、镜像
如果仓库X可以提供仓库Y存储的所有内容,那么就可以认为X是Y的一个镜像。
任何一个可以从仓库Y获得的构件,都能够从它的镜像中获取。
它会拦截maven对remote repository
的相关请求,把请求里的remote repository
地址,重定向到mirror
里配置的地址。配置mirror
的目的一般是出于网速考虑。
下面的xml示例中,<mirrorOf>的值为central
,表示该配置为中央仓库的镜像,任何对于中央仓库的请求都会转至该镜像,用户也可以使用同样的方法配置其他仓库的镜像。另外三个元素id、name、url与一般仓库配置无异,表示该镜像仓库的唯一标识符、名称以及地址。类似地,如果该镜像需要认证,也可以基于该id配置仓库认证。
<mirrors>
<!-- 配置镜像 --><mirror><id>epoint-nexus</id><name>Epoint Nexus</name><url>http://192.168.0.99:8081/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf></mirror></mirrors>
七、POM文件
Maven项目的核心是pom.xml。项目对象模型(POM project object model),定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等。
7.1、pom文件中主要标签的含义
7.1.1、<project>标签
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...
</project>
声明当前的项目构建信息。所有的配置和依赖信息都要在此标签内。
7.1.2、项目描述信息标签
<modelVersion>4.0.0</modelVersion><groupId>com.epoint.ztbpb</groupId><artifactId>ztb-qd-parent</artifactId><version>7.1.30.1</version><packaging>jar</packaging>
<modelVersion>
指定当前构建项目的模板版本,和超级POM一样。<groupId>
指定当前构建工程的组别ID,一般为公司网址的倒叙+业务名。<artifactId>
指定当前构建工程的名称。<version>
指定当前构建工程的版本号。主要分为release
版和snapshot
版。<packaging>
指定当前工程的打包类型。常用的有war
、jar
、pom
和maven-plugin
等。
其中通过<groupId>
、<artifactId>
和<version>
可以在Maven服务器上确定依赖。
7.1.3、<properties>标签
用来声明变量,如在标签内可以把版本号作为变量进行声明,后面dependency中用到版本号时可以用${变量名}
的形式代替,这样做的好处是:当版本号发生改变时,只有更新properties
标签中的变量就行了,不用更新所有依赖的版本号。
7.1.4、<dependencyManagement>标签、<parent>标签和<dependencies>标签
<parent>
标签:
引用parent
的子pom,可以继承parent
里的依赖,优点是可以消除重复引用。
但是可能有的子工程只需要父工程中的部分的依赖,此时parent
就显得无力。<dependencyManagement>
标签和<dependencies>
标签:
在Maven中<dependencyManagement>
的作用其实相当于一个对所依赖jar包进行版本管理的管理器。pom.xml
文件中,jar的版本判断的两种途径:
(1)如果<dependencies>
里的dependency
自己没有声明version
元素,那么maven就会到<dependencyManagement>
里面去找有没有对该artifactId
和groupId
进行过版本声明,如果有,就继承它,如果没有就会报错,告诉你必须为dependency
声明一个version
。
(2)如果<dependencies>
中的dependency
声明了version
,那么无论<dependencyManagement>
中有无对该jar的version
声明,都以dependency
里的version
为准。<dependencyManagement>
标签和<dependencies>
标签区别:
<dependencyManagement>
只是对版本进行管理,不会实际引入jar。
<dependencies>
会实际下载jar包。
八、依赖
8.1、依赖的原则
1、短路优先(就近原则)
C->B->A->X1(jar)
C->B->X2(jar)
C依赖B,B依赖A,A和B都包含同一个不同版本的Jar,则取B的依赖版本。(c的pom.xml中不必注明jar坐标)
2、先声明优先
如果路径相同长度相同,则谁先声明,先解析谁
C依赖A和B,A和B都包含同一个不同版本的Jar,谁依赖在前取谁的依赖版本
8.2、既然有了以上的规则,为什么项目有时还会报jar包冲突的错误?
常见原因:
(1)传递依赖导致不同版本jar包冲突,maven采用就近原则排除了依赖路径比较远的jar,如果排除的是新版本的jar包,而调用的方法是只有新jar中才有的,这样就会报错,一般是ClassNotFound这类的错误。
(2)不同的jar包,出现了相同的类路径,这种情况,会导jvm运行时不知道执行哪个类。 解决依赖冲突,需要根据实际需求排除无用的jar包,排出时无需指定版本号,示例:
<dependency><groupId>com.epoint.frame</groupId><artifactId>epoint-frame-action</artifactId><exclusions><exclusion><groupId></groupId><artifactId></artifactId></exclusion></exclusions>
</dependency>
8.3、依赖scope
<dependency>
中还引入了<scope>
,它主要管理依赖的部署:
compile
:编译依赖范围,适用于所有阶段,会随着项目一起发布,依赖范围默认值【会传递依赖】test
:测试依赖范围,测试时需要,只对测试的classpath有效,如junit。这些依赖不会被打包到最终的artifact中【不会传递依赖】provided
:已提供依赖范围,编译和测试时需要。运行时不需要,如servlet-api,因为在运行项目的时候,由于容器已经提供,就不需要Maven重复的引入一遍【不会传递依赖】runtime
:运行时依赖范围,测试和运行时需要。编译不需要,例如面向接口编程,JDBC驱动实现jar,写代码的时候用不到只在运行时用到,这些依赖将会被打包到最终的artifact中【会传递依赖】system
:系统依赖范围。本地依赖,不在maven中央仓库,结合systemPath标签使用,不推荐使用system依赖【会传递依赖】
system示例:
<dependency><groupId>dingding</groupId><artifactId>dingding</artifactId><version>2.8</version><scope>system</scope><systemPath>${project.basedir}/lib/taobao-sdk-java.jar</systemPath>
</dependency>
import
:从其它的pom文件中导入依赖设置,如:
<dependencyManagement><dependencies><dependency><groupId>com.epoint.frame</groupId><artifactId>epoint-dependency</artifactId><version>9.4.1-sp1</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
8.4、聚合与继承
8.4.1、聚合
如果想要一次构建两个项目,而不是两个模块目录下分别执行maven指令,这时候就需要使用Maven聚合。
使用Maven聚合的几个特点:
- pom.xml中
packaging
值必须为POM
,否则聚合POM会报错 - 模块使用
<modules>
标签和<module>
标签定义
为了方便定义,通常将聚合模块放在项目目录的最顶层,其他模块则作为聚合模块的子目录存在,可读性较高。但目录结构并非强制一定要是父子结构。
Maven首先会解析聚合模块的POM文件,分析要构建的模块,并计算出一个反应堆构建顺序,然后根据顺序依次构建各模块。反应堆是所有模块组成的一个构建结构。
另外,为了快速定位内容,模块所处的目录应当与其<artifactId>
一致,但不是强制要求;总之,<modules>
下的每个<module>
的值都是一个当前POM的相对目录。
8.4.2、继承
类型于面向对象设计中的继承,可以创建POM的父子结构,在父POM中声明一些配置供子POM继承,以实现“一处声明,多处使用”的目的。
继承的特点
- 父模块只是为了帮助消除配置重复,因此他本身不包含除POM之外的项目文件。
- 父模块的pom.xml的
packaging
值必须要为POM
,和聚合一样。否则子工程会报错。 - 子模块通过<parent>标签继承父模块POM
8.4.3、聚合和继承的关系
区别:
- 对于聚合模块来说,他知道有哪些模块被聚合,但对于被聚合的模块来说,不知道聚合模块的存在。
- 对于继承关系的父POM来说,他不知道有哪些子模块继承了它,子模块都知道父模块是什么。
相同点:
packaging
的值都是POM
- 除了POM之外都没有实际内容
为了方便,一个模块POM可以既是聚合模块POM,也是父模块POM。
九、生命周期
Maven有三大生命周期:clean
、default
和site
常用的是clean
和default
两个声明周期。
9.1、clean
生命周期
清理项目生产的临时文件,一般是模块下的target目录
pre-clean
预清洁 执行实际项目清理之前所需的流程
clean
清洁 删除以前构建生成的所有文件
post-clean
后清洁 执行完成项目清理所需的流程
9.2、default
生命周期
默认生命周期有很多,以下列出一些常用的:
validate
:验证这个项目是否正确,所有必需资源是否可用
compile
: 编译项目的源代码
test
: 运行所有单元测试例子
package
:打包编译后的代码成可发包格式,例如:jar,war等
verify
:做一些对包的验证操作,去检测这个包是一个合法的符合标准的包
install
:将包安装到本地仓库,提供给作为其他项目使用,例如:包的本地依赖
deploy
:最终的结果是部署到集成环境或者正式环境,复制这个最终版本到远程仓库并分享给其他项目或者开发者使用
Maven生命周期的步骤会默认把前面的步骤执行一遍。
9.3、maven打包时如何跳过测试?
- (1)
idea
在maven
里集成了skip tests
的功能,勾选即可。 - (2)
pom
中可以跳过,在<properties></properties>
中配置。
<properties><maven.test.skip>true</maven.test.skip><skipTests>true</skipTests>
</properties>
以上POM中的两个配置方式二选一,其中第一中不会编译源代码,第二种会编译源代码生成.class
文件,都不会执行测试。
十、Maven插件
10.1、插件解析机制
Maven本质上是一个插件执行框架,它的核心并不执行任何具体的构建任务,所有这些任务都交给插件来完成。
与我们所依赖的构件一样,插件也是基于坐标保存在我们的Maven仓库当中的。在用到插件的时候会先从本地仓库查找插件,如果本地仓库没有则从远程仓库查找插件并下载到本地仓库。
为了方便用户使用和配置插件,Maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件。比如:在用户没有提供插件版本的情况下,Maven会自动解析插件版本。
一般来说,中央仓库所包含的插件完全能够满足我们的需要,因此也不需要配置其他的插件仓库。
了解插件后续必须要了解两个概念,插件目标和插件绑定。
插件目标
插件目标可以理解为一个插件里的一个功能,一个插件里可能有多个功能,这些功能都聚集在一个插件里,每个功能就是一个插件目标。
插件绑定
Maven的生命周期与插件相互绑定,用以完成实际的构建任务。 具体而言,就是生命周期的阶段和插件的目标相互绑定。
10.1.1、插件的默认groupId
如果是maven的官方插件,pom.xml配置的时候可以省略groupId,Maven在解析的时候会自动补齐,但为了可读性不推荐这种做法。
10.1.2、解析插件版本
如果pom.xml配置时没有提供插件的版本,Maven会自动解析插件的版本。
另外超级POM中已经指定了核心插件的版本,所有pom都可以隐式继承。
如果用户使用某个插件没有指定版本,且不属于核心插件范畴,Maven会遍历本地仓库和所有远程仓库,计算出latest
和release
的值。
maven2采用的是latest
,即最新快照版。
maven3采用的是release
,即最新发布版。
10.2、插件与生命周期绑定
生命周期的阶段phase
与插件的目标goal
相互绑定, 用以完成实际的构建任务。一个插件可以实现多个目标(Goal)。
如:$ mvn compiler:compile
: 冒号前是插件前缀, 后面是该插件目标(即: maven-compiler-plugin
的compile
目标). 而该目标绑定了default
生命周期的compile
阶段:因此, 他们的绑定能够实现项目编译的目的。
Maven 默认为一些核心的生命周期绑定了插件目标, 当用户通过命令调用生命周期阶段时, 对应的插件目标就会执行相应的逻辑。
生命周期阶段 | 插件目标 | 执行任务 |
---|---|---|
clean | maven-clean-plugin:clean | 清理输出目录资源 |
compile | maven-compiler-plugin:compile | 编译主代码到主输出目录 |
test | maven-surefire-plugin:test | 执行测试用例 |
package | maven-jar-plugin:jar | 打jar包 |
install | maven-install-plugin:install | 将项目输出安装到本地仓库 |
deploy | maven-deploy-plugin:deploy | 将项目输出部署到远程仓库 |
10.3、常用插件
10.3.1、打包插件
maven-jar-plugin
,打包(jar)插件,设定MAINFEST.MF
文件的参数。
<!-- 可以从 SVN 中获取版本号,并将其变成环境变量,交由其他插件使用 -->
<plugins><plugin><groupId>com.google.code.maven-svn-revision-number-plugin</groupId><artifactId>svn-revision-number-maven-plugin</artifactId><version>1.13</version><executions><execution><phase>validate</phase><goals><goal>revision</goal></goals></execution></executions><configuration><entries><entry><prefix>svn</prefix></entry></entries></configuration><dependencies><dependency><groupId>org.tmatesoft.svnkit</groupId><artifactId>svnkit</artifactId><version>1.9.3</version></dependency></dependencies></plugin><!-- 构建生成时间戳 --><plugin><groupId>org.codehaus.mojo</groupId><artifactId>buildnumber-maven-plugin</artifactId><version>1.4</version><configuration><timestampFormat>yyyy-MM-dd HH:mm:ss.S</timestampFormat><timestampPropertyName>buildTime</timestampPropertyName></configuration><executions><execution><phase>validate</phase><goals><goal>create-timestamp</goal></goals></execution></executions></plugin><!--打包(jar)插件,设定 MAINFEST.MF文件的参数--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>${maven-jar-plugin-version}</version><configuration><archive><compress>true</compress><addMavenDescriptor>false</addMavenDescriptor><manifest><addDefaultImplementationEntries>true</addDefaultImplementationEntries><addDefaultSpecificationEntries>true</addDefaultSpecificationEntries></manifest><manifestEntries><SVN-Revision>${svn.revision}</SVN-Revision><SVN-CommittedRevision>${svn.committedRevision}</SVN-CommittedRevision><Build-Time>${buildTime}</Build-Time></manifestEntries></archive><excludes><exclude>**/allatori.xml</exclude><exclude>**/rebel.xml</exclude></excludes></configuration></plugin>
</plugins>
打包后的示例:
Manifest-Version: 1.0
Implementation-Title: BidStandardPBCore
Implementation-Version: 7.1.30.1
Archiver-Version: Plexus Archiver
Built-By: Faep
Specification-Title: BidStandardPBCore
Implementation-Vendor-Id: com.epoint.ztbpb
SVN-CommittedRevision: 15827
Build-Time: 2020-04-27 17:29:10.199
Created-By: Apache Maven 3.2.5
Build-Jdk: 1.8.0_60
Specification-Version: 7.1.30.1
SVN-Revision: 15828
属性解释:
- Manifest-Version:用来定义manifest文件的版本
- Implementation-Title:定义了扩展实现的标题
- Implementation-Version:定义扩展实现的版本
- Archiver-Version:存档版本
- Built-By:构建者(打包者)
- Specification-Title:定义扩展规范的标题
- Implementation-Vendor-Id:定义扩展实现的组织的标识
- SVN-CommittedRevision:SVN提交版本
- Build-Time:构建时间(打包时间)
- Created-By:声明该文件的生成者,一般该属性是由jar命令行工具生成的
- Build-Jdk:打包使用的JDK版本
- Specification-Version:定义扩展规范的版本
- SVN-Revision:本地SVN版本
10.3.2、Tomcat插件
在war工程的pom中
<build><plugins><!-- tomcat7运行插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>${tomcat7_maven_plugin_version}</version><configuration><path>/${project.artifactId}</path><port>8087</port><uriEncoding>${file_encoding}</uriEncoding><url>http://localhost:8087/</url><server>tomcat7</server></configuration></plugin></plugins>
</build>
运行插启动tomcat,执行命令mvn -tomcat7:run
10.3.3、编译插件
指定编译版本
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>${maven_compiler_plugin_version}</version><configuration><source>${java_version}</source><!-- 源代码使用的开发版本 --><target>${java_version}</target><!-- 需要生成的目标class文件的编译版本 --><encoding>${file_encoding}</encoding></configuration>
</plugin>
10.4、个性化插件
10.4.1、主要步骤说明
- 创建一个
maven-plugin
项目,插件本身也是maven
项目,区别就是packaging
标签是maven-plugin
。可以用maven-archetype-plugin
快速创建。 - 为插件写目标,每个插件都必须包含一个或多个目标,Maven称之为
Mojo
(Maven Old Java Object
),和Java的POJO对应。编写插件的时候必须提供一个或多个继承自AbstractMojo
的类。 - 为目标提供配置点,大部分的Maven插件及其目标都是可配的。
- 编写代码实现目标行为,根据实际的需要实现Mojo。
- 错误处理及日志,Mojo发生异常时,应该提供可靠的日志信息。
- 插件测试
10.4.2、案例
编写一个用于代码统计的案例:使用该插件,用户可以了解到Maven项目中各个源代码目录下文件的数量,以及它们加起来共有多少行代码
首先创建一个maven-plugin
项目,确保其pom中有如下内容:
<properties><maven.version>3.0</maven.version>
</properties>
<dependencies><dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>${maven.version}</version></dependency><dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-annotations</artifactId><version>3.2</version><scope>provided</scope></dependency><dependency><groupId>org.codehaus.plexus</groupId><artifactId>plexus-utils</artifactId><version>3.0.8</version></dependency>
</dependencies>
然后写一个Mojo类继承抽象类AbstractMojo
:
package com.epoint.maven.plugin.CodeCountPlugin;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.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;/*** 计算代码行数** @goal codecount*/
public class MyMojo extends AbstractMojo {private static final String[] INCLUDES_DEFAULT = { "java", "xml", "properties" };/*** @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*/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) {throw new MojoExecutionException("无法计算代码行.", e);}}private void countDir(File dir) throws IOException {if (!dir.exists()) {return;}List<File> collected = new ArrayList<>();collectFiles(collected, dir);int lines = 0;for (File sourceFile : collected) {lines += countLine(sourceFile);}String path = dir.getAbsolutePath().substring(basedir.getAbsolutePath().length());getLog().info("目录:" + path + ",有 " + collected.size() + "个文件, 共计" + lines + " 行代码 ");}/*** 递归收集目录下所有应该被统计的文件* @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*/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;}
}
每个插件目标类,或者说Mojo,都必须继承抽象类AbstractMojo
并实现execute()
方法,只有这样Maven才能识别该插件目标,并执行execute()
方法中的行为。
提供@goal
标注,任何一个Mojo都必须使用改标注写明自己的目标名称,有了目标定义后,才能在项目中配置该插件目标,或者在命令行调用它。
总结:
创建一个Mojo锁必要的三项工作:继承抽象类AbstractMojo
,实现execute()
方法,提供@goal
标注。
插件配置点
使用parameter
参数表示用户可以在使用该插件的时候在POM文件中配置改字段。如上述代码中的:
/*** 将包括在内的文件类型进行计数** @parameter*/
private String[] includes;
另外的说明:
@required
:表示该Mojo参数是必须的,如果使用了该标注,用户没有配置值且没有默认值的话会报错。@readonly
:表示该Mojo参数是只读的,如果使用了该标注,用户就无法对其进行配置。@parameter expression="${aSystemProperties}"
:使用系统属性表达式对Mojo属性赋值。
插件写好之后编译并使用maven install
安装到本地仓库后就可以在其他项目中使用。
如:
<build><plugins><plugin><groupId>com.epoint.maven.plugin</groupId><artifactId>CodeCountPlugin</artifactId><version>0.0.1-SNAPSHOT</version><executions><execution><goals><goal>codecount</goal></goals><phase>package</phase></execution></executions></plugin></plugins>
</build>
其中: <goal>
表示使用的插件目标,即代码中的@goal
标注定义的名称。
<phase>
表示使用插件的生命周期阶段,此处使用的是package
打包阶段。
针对上面所说的,使用parameter
参数表示用户可以在使用该插件的时候在POM文件中配置改字段。示例如下:
<build><plugins><plugin><groupId>com.epoint.maven.plugin</groupId><artifactId>CodeCountPlugin</artifactId><version>0.0.1-SNAPSHOT</version><configuration><includes><include>java</include><include>html</include></includes></configuration><executions><execution><goals><goal>codecount</goal></goals><phase>package</phase></execution></executions></plugin></plugins>
</build>
调用插件后,打包的输出会多出如下的行:
[INFO] --- CodeCountPlugin:0.0.1-SNAPSHOT:codecount (default) @ EpointPB.StandradCore ---
[INFO] 目录:\src\main\java,有 148个文件, 共计31072 行代码
[INFO] 目录:\src\test\java,有 0个文件, 共计0 行代码
[INFO] 目录:\src\main\resources,有 0个文件, 共计0 行代码
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.395 s
[INFO] Finished at: 2020-04-28T14:49:15+08:00
[INFO] Final Memory: 38M/307M
[INFO] ------------------------------------------------------------------------
十一、其他知识点
11.1、关于发布Jar包
发布Jar包到私服其实是发布了Jar包和POM文件两个单独的内容。可通过本地仓库发现这一点。
另外,在pom文件中<dependency>
标签下可通过<type>
指定引入的类型,比如jar
或pom
,默认是jar
。
11.2、POM中的<Profile>标签
主要用于针对不同环境的构建
11.3、<parent>
标签中的<relativePath>
标签
<relativePath>
表示父模块POM的相对路径,当项目构建时,Maven会首先根据relativePath
检查父POM,如果找不到就从本地仓库找。
relativePath
的默认值是../pom.xml
,Maven默认父POM在上一层目录下。
关于Maven,这些应该够用了相关推荐
- apache camel 相关配置_Web基础配置篇(二): Maven配置及使用
Web基础配置篇(二): Maven配置及使用 一.概述 Maven是一个软件开发管理工具,主要管理工作是:依赖管理,项目一键构建. 以前用过ant,很不方便,maven比较简单易用. 然后后面又来了 ...
- Web基础配置篇(二): Maven配置及使用
Web基础配置篇(二): Maven配置及使用 一.概述 Maven是一个软件开发管理工具,主要管理工作是:依赖管理,项目一键构建. 以前用过ant,很不方便,maven比较简单易用. 然后后面又来了 ...
- Linux就这个范儿 第8章 我是Makefile
Linux就这个范儿 第8章 我是Makefile P287 Makefile的作用就是--自动化编译,一旦写好,只需要一个make命令(解析Makefile,执行Makefile中描述的操作),整个 ...
- Spring Cloud连载(2)搭建开发环境
本站小福利 点我获取阿里云优惠券 原文作者:杨大仙的程序空间 2 开发环境搭建 工欲善其事,必先利其器.在讲述本书的技术内容前,先将开发环境搭建好,本书所涉及基础环境将在本章准备,包括Eclipse. ...
- Maven实战:Maven生命周期
前言 之前有写过一篇文章Maven实战,介绍了Maven的一些基本概念,以及对于一个初学者而言的Maven基础知识,当时在我看来掌握了这些基本是够用的. 随着工作的深入,越来越感觉对于Maven的理解 ...
- maven POM.xml 标签详解
pom作为项目对象模型.通过xml表示maven项目,使用pom.xml来实现.主要描述了项目:包括配置文件:开发者需要遵循的规则,缺陷管理系统,组织和licenses,项目的url,项目的依赖性,以 ...
- Maven使用笔记(四)pom.xml配置详解
--声明规范 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3 ...
- Maven 私服搭建指南
序言 Maven 作为 Java 项目管理工具,它不仅可以用作包管理,还有许多的插件,可以支持整个项目的开发.打包.测试.部署等一系列行为. 而包管理又是其核心功能,除非是个人项目,我们获取包都是从中 ...
- Java基础-Eclipse第三方安装包管理工具之Maven
Java基础-Eclipse第三方安装包管理工具之Maven 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 玩过Linux的小伙伴应该都知道yum吧,我们只要把搭建好的yum仓库配 ...
- Maven pom.xml 全配置(一)常用配置
Maven pom.xml 全配置(一)常用配置 这里贴出一个Maven中出现频率较高的配置参数注释,方便理解项目中Maven的配置具体的作用.如果在此博文中没有找到你想看到的参数,可以移步Maven ...
最新文章
- 安装完Arch后,要安装的软件
- 独木舟上的旅行http://acm.nyist.net/JudgeOnline/problem.php?pid=71
- P3067 [USACO12OPEN]Balanced Cow Subsets G 折半搜索
- android外置sd大小,android 读取外置和内置存储卡路径和大小
- 两用物项许可证办理流程_一指通 | 出口许可证办理流程
- QQ登录JS SDK教程,调用openapi接口
- 多线程的那点儿事(之原子锁)
- gulp错误GulpUglifyError: unable to minify JavaScript解决
- python期末考试及答案广东_PYTHON语言应用答案试题题目及答案,期末考试题库,章节测验答案...
- 6个html5手机游戏源码,html5逗你玩手机游戏源码
- 软件测试笔记2-目的
- Youtube视频码率,帧率,分辨率那些事
- 实战演习(二)——网站点击流数据分析
- Testin云层天咨众测学院开课了!
- 第四届蓝桥杯JavaC组国(决)赛真题
- android图片分辨率改变,Android实现改变一个图片的像素值
- VB中使用表查询法获取CRC16
- matlab的基本用法---常用的输入输出函数
- 嵌入式系统的数据结构与算法
- 路由跟踪工具——笨鸟
热门文章
- 万用表二极管档位点亮发光二极管LED
- django3.1发送邮件指定html正文:content_subtype = “html“
- 多视点视频编码快速模式选择算法综述
- 第五十三回 关云长义释黄汉升 孙仲谋大战张文远
- gnu coreutils4.5.1 hostid.c源码解读
- IntelliJ IDEA 12.0搭建Maven Web SSH2架构项目示例(二)
- 123321是一个非常特殊的数,它从左边读和从右边读是一样的。输入一个正整数n, 编程求所有这样的五位和六位十进制数,满足各位数字之和等于n 。
- 第三章:用python实现常用的用户分层模型(RFM模型)
- Python学习(列表)
- flutter设置签名