Maven作为Java语言的构建和依赖管理工具,已经被广泛使用。但对于maven的pom.xml的配置以及插件的使用,大部分人也仅仅限于了解的程度。工欲善其事,必先利其器。在拖延了很久后,决定还是花时间根据《maven实战》这个书来进一步深入了解maven的使用。这个系列是在学习maven过程中的笔记和总结,聊以记录,利人利己。
maven坐标定义
- groupId:当前maven项目隶属的实际项目,不止要到公司或组织,而应该到实际项目,应为项目往往会划分成多个模块,如org.sonatype.nexus,org.sonatype是一个组织,nexus是一个实际项目
- artifactId:实际项目中的一个maven项目(模块),推荐使用实际项目名作为artifactId的前缀,比如上面的nexus的索引模块可为nexus-indexer,这样可区分不同实际项目的一些基础模块,比如base或core等模块
- version:版本
- packaging:maven项目的打包方式,jar打包和war打包构建的生命周期会有不同的命令,默认为jar
- classifier:用来帮助定义构建输出的一些附属构件,如javadoc或sources。注意,不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成的。
项目构建的文件名是与坐标相对应的,一般的规则是artifactId-version[-classifier].packaging,[-classifier]为可选
maven依赖配置
pom.xml中<dependencies>标签下包含一个或多个<dependence>元素,来声明一个或多个项目依赖。每个依赖可以包含的元素有
- groupId、artifactId和version:依赖的基本坐标
- type:依赖的类型,对应项目坐标的packaging
- scope:依赖范围
- optional:标记依赖是否可选
- exclusions:用来排除传递性依赖
依赖范围
- compile:scope默认依赖范围,对于编译,测试,运行三种classpath都有效,如spring-core
- test:只对于测试classpath有效,如JUnit
- provided:对编译和测试classpath有效,运行时无效。如servlet-api,因为运行时容器已经提供,不需要maven重复引入
- runtime:测试和运行时classpath有效如JDBC驱动实现,编译时只需要JDK提供的JDBC接口
- system:同provided范围一致,使用system范围的依赖不是通过maven仓库解析,而是通过systemPath元素显示指定依赖文件。因为可能造成构建的不可移植,应谨慎使用。
依赖传递
如果项目的依赖有自己的依赖,则项目也会加载依赖的依赖。比如spring-core依赖commons-logging,如果项目依赖了spring-core,那么它也会依赖commons-logging。当依赖的声明为可选时(<optional>true</optional>),依赖不会传递。
依赖调解
因为存在依赖传递,就会有不同的依赖中有不同版本的基础依赖,如A -> X(1.0),B -> C -> X(2.0),这样X就有不同的版本,哪个会被maven解析呢?于是就有了依赖调解的两个原则:
- 路径最近者优。,如上X(1.0)的路径为1,而X(2.0)的路径为2,因此X(1.0)会被使用
- 同等路径下,第一声明者优先。在路径长度都一致时,pom.xml中顺序靠前的被使用。
依赖排除
项目中可能存在,因为一些原因,不想引入某依赖的传递性依赖,则可以使用exclusions。比如在使用spring时不想用commons-logging,则可以在spring-core中排除。
org.springframework spring-core 5.0.0.M5 commons-logging commons-logging
依赖版本归类
比如在使用spring时,可能会用到spring-context,spring-jdbc,spring-tx等,但他们的版本是一致的,考虑到以后版本的升级,可以定义一个spring.version的properties来统一设置版本。
4.1.6.RELEASE
而在依赖的<version>中用${spring.version}代替
org.springframework spring-context ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-tx ${spring.version}
依赖查看与分析
// 查看项目依赖列表mvn dependency:list// 以树状显示依赖列表mvn dependency:tree// 依赖分析mvn dependency:analyze
maven生命周期
maven有三套相互独立的生命周期,分别是clean,default,site。
- clean的目的是清理项目
- default的目的是构建项目
- site的目的是建立项目站点
每个生命周期包含一些阶段(phase),这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段。default是生命周期中最核心的部分,它包含很多阶段,以下是常见的几种阶段,全量的阶段可见
- validate:验证项目是否正确
- compile:编译项目源代码
- test:使用一个测试套件测试编译后的代码
- package:打包项目代码
- verify:检查集成测试结果
- install:将打包好的项目代码安装到本地仓库
- deploy:复制最后的打包结果到远程仓库
我们可以用命令行来执行生命周期阶段
- mvn clean:调用clean生命周期的clean阶段,包含pre-clean和clean阶段
- mvn test:调用default生命周期的test阶段,包含validate到test的所有阶段
- mvn clean install:调用clean生命周期的clean阶段和default生命周期的install阶段,实际执行的是clean生命周期的pre-clean,clean阶段和default生命周期从validate至install的所有阶段
maven插件
插件目标
我们知道maven的核心只定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构件存在,maven会在需要时下载并使用插件。对于一个插件,可能会有多个功能,每个功能就对应一个插件目标(Plugin Goal)。
比如maven-dependency-plugin有多个目标,如dependency:list,dependency:tree,dependency:analyze等,冒号前是插件前缀,冒号后是插件目标。类似的,compiler:compile(maven-compiler-plugin的compile目标)和surefire:test(maven-surefire-plugin的test目标)。
在使用时,需要将生命周期的阶段和插件目标相互绑定,以完成某个具体的构建任务。如maven-compiler-plugin的compile目标绑定的就是default生命周期的compile阶段。
maven内置了很多生命周期阶段同插件目标的绑定,比如打包类型为jar的default生命周期的内置插件绑定关系如下:
生命周期阶段 | 插件目标 | 执行任务 |
---|---|---|
process-resources | maven-resources-plugin:resources | 复制主资源文件至主输出目录 |
compile | maven-compiler-plugin:compile | 编译主代码至主输出目录 |
process-test-resources | maven-resources-plugin:testResources | 复制测试资源文件至测试输出目录 |
test-compile | maven-compiler-plugin:testCompile | 编译测试代码至测试输出目录 |
test | maven-surefire-plugin:test | 执行测试用例 |
package | maven-jar-plugin:jar | 创建项目jar包 |
install | maven-install-plugin:install | 将项目输出构件安装到本地仓库 |
deploy | maven-deploy-plugin:deploy | 将项目输出构件部署到远程仓库 |
出了内置的绑定关系外,用户可以选择将某个插件目标绑定到生命周期的某个阶段。一个例子是创建项目的源码jar包,可以使用maven-source-plugin的jar-no-fork目标奖项目主代码打成jar文件,我们将其绑定到verify阶段。
org.apache.maven.plugins maven-source-plugin 3.0.1 attach-sources verify jar-no-fork
有很多插件的目标在编写时已经定义了默认绑定阶段,可以使用命令查看
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1Ddetail
可以看到输出的信息中有
source:jar-no-fork...Bound to phase: package
即jar-no-for默认绑定的生命周期阶段为package
插件配置
用户可以通过命令行和pom配置来配置插件的参数
1.命令行配置,用户可以在maven命令中使用-D参数,指定一个key-value的形式,来配置插件目标的参数。例如maven-surefire-plugin提供了一个maven.test.skip参数,当为true时,就跳过执行测试。
mvn install -Dmaven.test.skip=true
2.pom中插件全局配置,所有基于该插件目标的任务都会使用此配置,常见的youmaven-compiler-plugin编译java的版本。
org.apache.maven.plugins maven-compiler-plugin 2.3.2
3.用户可以为某个插件任务配置特定的参数,将maven-antrun-plugin:run绑定到多个生命周期阶段上,加以不同的配置
maven-antrun-plugin 1.8 ant-validate validate run validate phase ant-verify verify run verify phase
插件查找
可以从
//查询插件信息mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1//查询插件目标信息mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1 -Dgoal=jar-no-fork//查询详细信息mvn help:describe -Dplugin=org.apache.maven.plugins:maven-source-plugin:3.0.1 -Ddetail
Maven内置变量
Maven内置了一些目录变量,对涉及文件路径的配置会有帮助
- ${basedir} 项目根目录
- ${project.build.directory} 构建目录,缺省为target
- ${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes
- ${project.build.finalName} 产出物名称,缺省为${project.artifactId}-${project.version}
- ${project.packaging} 打包类型,缺省为jar
- ${project.xxx} 当前pom文件的任意节点的内容