使用maven-pom进行依赖管理与自动构建html
最后编辑于 :pencil::{docsify-updated}
java
随着开发生态环境的不断发展,几乎全部的java应用都会使用第三方的类库,尤为是在这个开源的世界里, java应用依赖管理已经很难再由人工完成——依赖管理包括解决依赖传递、版本冲突、依赖臃肿等问题。git
maven经过groupId
、artifactId
、version
造成的坐标定位系统能准确的定位每个构件(artifact), 开发者能够经过在pom文件中列出所依赖的构件的坐标,让maven工具从maven仓库中自动下载全部须要的构件; 另外一方面,经过pom文件间的依赖传递、继承等方式下降依赖管理的难度。github
在咱们的开发过程当中,除了编写代码之外,有很大一部分时间是花在编译、运行单元测试、生成文档、打包和部署这些工具上, 为也提升工做效率,使开发人员能更多的将精力用于开发工做,咱们须要像maven这样的工具, 如流水线般的将全部部署以自动化的方式完成。web
在java开发中,经常使用的构建工具备Ant、maven、gradle三种。spring
Ant是过程式的,开发者须要显示的指定每一个目标,以及完成该目标锁须要执行的任务。shell
Ant不只限于对java项目进行构建,也能够对其余语言(如C语言)的项目进行构建。apache
<project name="MyProject" default="dist" basedir="../../../../../../../ProgramFiles/JetBrains/IntelliJ IDEA 2019.3.2/jbr/bin"> <description> simple example build file </description> <!-- set global properties for this build --> <property name="src" location="src"/> <property name="build" location="build"/> <property name="dist" location="dist"/> <target name="init"> <!-- Create the time stamp --> <tstamp/> <!-- Create the build directory structure used by compile --> <mkdir dir="${build}"/> </target> <target name="compile" depends="init" description="compile the source"> <!-- Compile the Java code from ${src} into ${build} --> <javac srcdir="${src}" destdir="${build}"/> </target> <target name="dist" depends="compile" description="generate the distribution"> <!-- Create the distribution directory --> <mkdir dir="${dist}/lib"/> <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file --> <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/> </target> <target name="clean" description="clean up"> <!-- Delete the ${build} and ${dist} directory trees --> <delete dir="${build}"/> <delete dir="${dist}"/> </target> </project>
与maven、gradle不一样,ant自己没有对应的中央仓库,若是想使用maven同样, 经过声明的方式管理依赖,并自动处理依赖管理等问题,则使用集成ivy插件管理依赖。 但ant不支持多模块的管理方式,而maven和gradle支持。windows
ant的这种声明方式,使开发者能够根据本身的须要灵活配置,定制本身的项目构建流程; 另外一方面,这种灵活的配置方式,也使得ant的配置相对繁琐,可读性较差,学习成本也更高 ——事实上,ant的官方网站上列出的插件就有139种,并且部分工具的官网已经404了。api
maven与gradle同样,都是声明式的配置方式,相对于Ant来讲,配置更加方便。 同时maven有内建生命周期,约定了项目的代码结构,只须简单配置,就能够完成构建任务。
顶级pom中定义的项目结构(即默认项目结构)
项目根目录 |-- src | |-- main | | |-- java -> 主代码文件 | | |-- resources -> 主资源文件 | | -- scripts -> 虽然在maven的默认结构里有这个目录,但官网文档中已删除了这一级目录,不建议使用 | -- test | |-- java -> 测试代码文件 | -- resources -> 测试资源文件 -- pom.xml
小提示:可经过mvn -DarchetypeCatalog=internal archetype:generate
初始化标准结构的maven项目
另外一方面,maven内建生命周期,使用没法更加项目的构建顺序,若是想在构建中加上一些其余处理逻辑, 则须要用编写maven插件的方式来完成,成本相对高昂。
gradle是相对新颖的构建工具,它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置, 目前也增长了基于Kotlin语言的kotlin-based DSL,抛弃了基于XML的各类繁琐配置。
gradle没有maven般的生命周期,真正起做用的是所引入的Plugin,所以相对更为灵活。
另外一方面,gradle做为新生工具,不管是程序仍是文档都在不断的完善中,使用和学习相对困难。 同时,因为gradle的配置是基于groovy/kotlin语法的,所以使用gradle须要掌握相应的语法知识。
gradle是对ant和maven特色折衷的结果,相对于ant其可读性更好,相对于maven其更为灵活。
POM是project object mode的简写,maven经过pom文件对项目进行描述, 在开发者在pom文件中对项目属性和构建过程进行定义后,maven便可自动构建项目并生成站点。
<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"> <modelVersion>4.0.0</modelVersion> <!-- The Basics --> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <packaging>...</packaging> <dependencies>...</dependencies> <parent>...</parent> <dependencyManagement>...</dependencyManagement> <modules>...</modules> <properties>...</properties> <!-- Build Settings --> <build>...</build> <reporting>...</reporting> <!-- More Project Information --> <name>...</name> <description>...</description> <url>...</url> <inceptionYear>...</inceptionYear> <licenses>...</licenses> <organization>...</organization> <developers>...</developers> <contributors>...</contributors> <!-- Environment Settings --> <issueManagement>...</issueManagement> <ciManagement>...</ciManagement> <mailingLists>...</mailingLists> <scm>...</scm> <prerequisites>...</prerequisites> <repositories>...</repositories> <pluginRepositories>...</pluginRepositories> <distributionManagement>...</distributionManagement> <profiles>...</profiles> </project>
声明式的项目依赖管理,除了须要一个存储有全部jar包的中央仓库外,还须要一套合适的命名系统, 可以保证每一个jar包都有一个惟一的名字,包括不一样版本。
在maven中,jar包的坐标经过groupId
、artifactId
、version
三个字段决定, 相似于xyz坐标系的三个轴。
<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"> <modelVersion>4.0.0</modelVersion> <groupId>org.codehaus.mojo</groupId> <artifactId>my-project</artifactId> <version>1.0</version> </project>
一般而言,一个组织(公司、基金会等)的项目一般使用同一个groupId, 所以通常而言groupId是该组织所拥有的域名,如jacoco对应的groupId为org.jacoco
, 而对应的网站为http://jacoco.org
(域名的优先级顺序是反过来的),
事实上不必定严格遵循该规则,如maven的groupId为org.apache.maven
, 而maven官方插件的groupId为org.apache.maven.plugins
, 尽管同并不存在plugins.maven.apache.org
这个四级域名, 并且他们同属于apache基金会(apache.org)下的项目。
另外也有彻底和域名不相关的,如junit包的groupId为junt
。
另外须要注意的一点是,虽然groupId没必要与项目的包结构相对应,可是遵循这种作法是一种最佳实践, 由于这样能够有效避免包之间的类冲突。
artifactId一般是已知项目(或项目模块)的名称,它与groupId一块儿构成了一个项目(或项目模块)的定位, 用于将该项目(或项目模块)与世界上其余全部项目(或项目模块)分开。
实践
虽然在groupId中就能够看到项目名称,但在artifactId中通常仍会将项目名称做为开头, 这是由于咱们在沟通时一般会用 artifactId 进行交流,而不会带上 groupId , 如张三告诉李四须要在项目中引入maven-enforcer-plugin
包作项目自动检查。
如同字面的含义,version表明的是项目(或项目模块)的版本。
groupId
、artifactId
、version
一同定位了一个发布包的惟一位置。 如maven-enforcer-plugin-3.0.0-M3.jar在仓库中的位置为 $M2_REPO/org/apache/maven/plugins/maven-enforcer-plugin/3.0.0-M3
, 而其对应的pom中的内容以下:
<parent> <groupId>org.apache.maven.enforcer</groupId> <artifactId>enforcer</artifactId> <version>3.0.0-M3</version> </parent> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId>
parent标签咱们后续会提到,parent标签下是父pom的坐标,子pom继承父pom的全部定义,包括version
package标签定义了构件的打包方式和使用方式,若是不指定值,默认使用jar做为打包方式。
这里只说明两个咱们使用较多的打包方式:pom、jar。
定义打包方式为pom是有一些特殊的项目,其自己除了pom文件外,不包含其余文件, 这些项目中的pom主要是用来被其余项目引用、继承,或者用来管理项目拥有的模块。
这里的继承概念与java中类的继承概念相似,子pom拥有父pom的全部配置, 能够经过自定义的方式覆盖/复写父pom中已经存在的配置。
咱们拿springboot集成中的两个打包方式为pom的项目举例说明。 在springboot的官方集成教程中,会告诉你可使用两种方式集成,一种经过父pom继承:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> </parent>
另外一种是经过引入spring-boot的依赖管理:
<dependencyManagement> <dependencies> <dependency> <!-- Import dependency management from Spring Boot --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.1.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
若是咱们找到spring-boot-starter-parent项目,就会发现这个项目里仅仅只有pom文件
这是由于在项目中,咱们一般会存在多个子模块,而这些子模块会须要一些公用的配置, 好比统一的构建流程、统一的依赖版本管理,而这些内容就以pom的形式抽取出来, 造成了这种打包方式为pom的项目。
须要注意的是,打包方式为pom的项目里,除了pom文件,其余文件不会参与构建。 事实上若是咱们去看pom打包对应的默认插件配置就会发现,只有install和deploy两个生命周期有插件绑定:
<phases> <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>
jar包是java程序包经常使用的发布形式,其能够做为库文件被其余项目使用, 也能够经过java -jar
命令直接运行。
jar包中即有静态资源,也有编译后生成的class文件,这意味着其打包流程也更为复杂:
<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>
目前maven支持这些打包方式: pom、jar、maven-plugin、ejb、war、ear、rar。 其中每种打包方式都有相应的打包插件对其进行打包,这些插件都已经被maven默认进行了绑定, 具体打包方式对应的插件配置有兴趣的能够去 官网进行查看。
在上文中咱们提到了多种项目间关系,包含依赖关系、继承关系、聚合关系(上文提到的模块管理)。
项目间依赖关系是最经常使用的关系,当咱们项目A使用一个项目B打成的jar时,A就对B造成了依赖。
须要注意的是,若是的java项目开发已经没法离开依赖了,A所依赖的项目B,实际上也开发中也依赖了项目C。 此时就生成的依赖的传递,即若是A依赖了B,B依赖了C,那么A依赖C。
依赖传递的存在,会使得单点的依赖关系也变得复杂起来。实际在项目中,咱们会依赖大量的jar包, 此时就可能存A依赖的B、C同时使用两个不一样版本的D:
A |-B |-D:1.1 |-C |-D:1.2
咱们知道,一个项目的不一样版本一般会有大量的类是重复了,若是jvm在加载同名类时,会忽略重复的类, 这样可能致使出现其中一个包只有一半代码的状况,这明显是不合理的。这种依赖的出现就叫到依赖冲突, 当出现冲突时,maven会选取其中的一个版本放入最终的打包后文件中(如jar),而忽略其余版本。
具体的选取策略能够参考这篇短文
继承关系咱们在上文中已经提到,咱们能够经过parent标签引入父级pom, 此时子pom拥有了父级pom中的全部定义,并能够在子pom中复写相应的定义。
因为pom的继承关系可能有多级,所以若是你想确认当前项目的最终pom结构时, 就必需要将全部的pom整合到一块儿,此时咱们能够经过mvn help:effective-pom
命令生成整合后的pom (若是使用idea,右键maven菜单下也有相应的生成操做)。
若是在一个没有父pom的项目中执行mvn help:effective-pom
,你会发现最终生成的pom中, 忽然出现了大量你没有定义的内容,这是所以maven自身定义了一个super pom, 全部的pom都默认继承该pom(可类比java中的Object类), 在上文中咱们提到的maven项目的默认结构,就是在这里定义的。
super pom的具体内容,能够去官网查看
子pom并不会继承父pom的全部内容,仅如下内容将会被继承: - groupId - version - scm - issueManagement - ciManagement - mailingLists - properties - dependencyManagement - dependencies - repositories - pluginRepositories - build - reporting - profiles - description - url - inceptionYear - organization - licenses - developers - contributors
须要注意的是如下几个标签不会被继承: - artifactId - package - name - prerequisites
聚合关系经常出如今有多个模块的项目中,好比项目A有三个子项目aa、ab、ac, 这时咱们能够在目录A下也添加一个pom,并定义aa、ab、ac是项目A的三个子项目, 这样咱们对项目A作构建(如mvn clean package)时,maven就会分别对aa、ab、ac作相应的构建操做。
A |-- pom.xml |-- aa |-- pom.xml |-- ab |-- pom.xml |-- ac |-- pom.xml
子项目的声明方式以经过modules标签进行,当咱们在项目A的pom中添加以下配置, aa、ab、ac就会成为A的子项目:
<modules> <module>aa</module> <module>ab</module> <module>ac</module> </modules>
aa、ab、ac并非这三个项目的相对路径(相对于A)
在这里,可能有的人已经注意到了,这种项目层级结构,很是适合将A项目的pom做为父pom, 将aa、ab、ac的pom中相同的内容抽取至父pom中进行管理。
实际上在项目的实践中,一般也是这么处理的,但也有一些例外。好比aa、ab是springboot项目, 而ac倒是老旧的spring项目,此时可能出现:A做为aa和ab的父级pom统一管理相关配置, 同时aa和ab做为子项目存在,而ac不继承A,只做为子项目存在,这样咱们即可以统一管理aa和ab的配置, 也可以经过一条命令,对aa、ab、ac一块儿完成构建操做。 (实际实践过程当中,不推荐这种模式,咱们能够把ac提到与A平级的位置,这样的结构更清晰)
值得一提的是,若是aa与ab间存在依赖关系,好比aa依赖于ab,那么即便aa比ab先声明,maven在构建时, 也会很聪明的先对ab进行构建,再构建aa。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <type>jar</type> <scope>test</scope> <optional>true</optional> </dependency> </dependencies>
dependencies标签下声明了当前项目所使用的依赖构件。
须要注意的是,这里声明的文件必须可以在maven仓库中找到, maven默认使用中央仓库,中央仓库中包括了全部经常使用的开源jar包, 但对于一些闭源的三方jar包,就没法在中央仓库中找到。
所以,公司通常都会有本身的私有仓库,咱们须要修改maven的settings.xml文件, 使maven使用私服处理依赖和发布。
对于私有仓库上没有的项目,咱们可能经过如下命令,将其上传至私有仓库:
shell script mvn deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group \ -DartifactId=non-maven-proj -Dversion=1.0 -Dpackaging=jar
在引用依赖时,仅仅使用groupId
、artifactId
、version
做为坐标可能还并不太足够。 考虑一下这种场景:有一个项目,该项目须要提供一个基于jre1.7的jar包, 同时还提供了一个基于jre1.8的jar包,即一套源码须要使用两种jdk打成不一样的jar包。
这种状况下咱们会配置两套打包方式,默认使用1.8打包,并可选择1.7打包,配置以下:
<profile> <id>jdk17</id> <properties> <!--使用jdk1.7打包--> <java.version>1.7</java.version> </properties> <build> <plugins> <plugin> <artifactId>maven-jar-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>jar</goal> </goals> <configuration> <!--配置classifier,与1.8版本的包区分--> <classifier>jdk17</classifier> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile>
若是用1.8打包发布的jar名称为demo-1.0.jar
,那么用1.7打包时, 发布的jar的名称为demo-1.0-jdk17.jar
——实际场景下,classifier标签能够配置成任意字符串, 并不必定是jdk17。
而使用者在引入1.7版本的依赖时,就须要额外添加classifier标签。
<dependency> <groupId>some.example</groupId> <artifactId>demo</artifactId> <version>1.0</version> <classifier>jdk17</classifier> </dependency>
classifier标签有一处更常见的地方,是sources.jar
:
这就是咱们上文中说到的一个pom,打出了两个包,源码包对应的classifier标签就是sources
, 而测试源码包对应的值为test-sources
。
type标签描述了依赖类型,若是不定义该值,使用默认值jar。
对于不一样类型的依赖,maven的处理策略也不相同:
type | classifier | extension | packaging | language | added to classpath | includesDependencies |
---|---|---|---|---|---|---|
pom | = type | = type | none | |||
jar | = type | = type | java | true | ||
test-jar | tests | jar | jar | java | true | |
maven-plugin | jar | = type | java | true | ||
ejb | jar | = type | java | true | ||
ejb-client | client | jar | ejb | java | true | |
war | = type | = type | java | true | ||
ear | = type | = type | java | true | ||
rar | = type | = type | java | true | ||
java-source | sources | jar | = type | java | ||
javadoc | javadoc | jar | = type | java | true |
实际开发中,咱们只会用到jar
和pom
两种类型,你们能够注意到pom
类型的依赖是不会产生依赖传递, 也不会被加入classpath中。
该标签订义了该包在哪些阶段会被放到classpath中,另外还定义了依赖的可传递性。其聚会范围以下:
classpath即类、类库加载路径,在咱们的依赖中,大多数依赖会在编译、测试、运行都用到, 但也有一些依赖可能只有在编译、测试阶段运到(好比测试相关的包),还有部分包可能在编译、测试时须要, 但实际发布时不须要(好比jdk相关的包)。
值 | 含义 | 是否会将该依赖向下传递 |
---|---|---|
compile | 该值是默认值。该依赖在全部阶段的都会放入classpath中,是最经常使用的值。 | 是 |
provided | 只有在编译和测试时放入classpath中,实际打包时忽略该包 | 否 |
runtime | 编译时不须要,只在测试和打包时须要 | 是 |
test | 只在对测试代码作编译和执行时须要 | 否 |
system | 手动指定所依赖包的本地存在路径 | 只传递自己 |
commons-aap-appconfig
包加上provided范围。 若是某个使用者只不须要使用TapConfigUtil,那么他就不须要引入commons-aap-appconfig
包。 (事实上commons-aap-appconfig
包里引入spring相关的依赖里使用了provided范围) systemPath
标签使用,用来指定使用仓库中没有的包。 与其相比,上传私服是一种更好的方式,由于system标记的包,并不会将其依赖的包传递给使用者, 这意味着你须要本身处理好依赖关系。 仅在依赖项范围是system时使用,不然构建将失败。
systemPath标签中的路径必须是绝对路径,所以建议使用maven变量拼装路径, 例如${project.basedir}/lib。
在上面介绍scope标签的provided取值时,咱们讲了它能够实现让使用都按需决定是否引入依赖, 这样以达到避免引入不须要的依赖。
事实上provided更多的是用来要求使用方或运行时系统提供具体的包,而不是用于这种用途。
配置依赖的可选应该使用optional标签来实现。
dependency标签下还存在exclusions标签,咱们在前面讲到了依赖传递可能带来的依赖冲突问题, exclusions标签就是排除掉不想被传递过来的依赖。
这里须要注意的是,A->B->C时,若是你在引入依赖A的时候,将B排除,那么C也将被排除, 除非有其余地方也引入了C(有点像对“树”进行操做,排除进至关于直接剪掉了一个“子树”)
<exclusions> <exclusion> <groupId>org.objenesis</groupId> <artifactId>objenesis</artifactId> </exclusion> </exclusions>
exclusion下只有groupId与artifactId两个标签,由于只不可能同时依赖一个包的不一样版本。
version标签除了指定具体版本外,还能够指定范围版本,有兴趣的能够查看 官网, 不过不推荐在项目中使用。
各小版本在maven项目中的前后发布顺序能够看 这里
正如其名字所表明的含义,该标签的做用就是管理依赖。
在上面的dependency标签的介绍中,咱们看到在dependency标签下有众多的子标签, 想象一下咱们有多个服务,若是每一个服务都在引入同一依赖时配置好全部内容, 那将是很是麻烦,并且若是须要对某个所依赖的jar包作升级时,咱们不得不改每个pom中的version信息。
可能有人已经想到,咱们能够将这些依赖放到父pom里面就能够了。这是一种很好的策略, 但让咱们考虑项目A、B,A中引入了a、b三个包,B中引入b、c两个包, 若是咱们使用父pom管理公用依赖,将只有b会被放到父pom里,而a、c由A、B本身管理。 若是后来B项目的功能增长,也须要引入a包,那么咱们就须要把a包从A项目中提到父pom中, 以保证对依赖的统一管理——由于,这种方式并不能真正实现依赖的统一管理。
如今考虑另外一种方式,咱们在父pom里列出项目中全部使用到的依赖包,并配置好全部内容, 但并不真正的对这些包产生依赖,仅仅是列出来。若是那个子pom真正须要用到, 那么他只须要声明一些尽量少的信息,就能够对对应的包产生依赖 ——这种方案就是使用dependencyManagement标签。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring.boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.demo.company</groupId> <artifactId>someproject-commons-versions</artifactId> <version>1.0.0</version> </dependency> </dependencies> </dependencyManagement>
dependencyManagement标签下只有一个dependencies标签, 里面的内容和上文中的dependencies标签几乎同样,惟一不同的是, 在dependencyManagement标签下的scope标签新增一种取值——import。
import只能用于导入package值为pom的依赖包, 当在项目A的pom中使用import导入项目B时,A、B的dependencyManagement标签下的内容将被合并。 这是一个很是有用的特性,这意味着咱们能够建立一个项目专门用来作统一版本控制, 在这个项目里,咱们将全部的包的version、scope等内容都定义好, 其余项目只须要在dependencyManagement下导入该包,而不须要破坏其自己的继承结构。
虽然dependencyManagement有这样的方便, 但须要注意的是dependencyManagement下的内容不只对当前pom、子pom生效, 一样会覆盖因为依赖传递而引入的jar包。好比在项目A中,咱们依赖了aa-1.0、bb-2.0, 而在bb-2.0中依赖了aa-2.0,此时,若是aa的版本是在dependencyManagement中定义, 那么aa-2.0将被放弃,aa-1.0生效,这样将致使bb-2.0可能使用了根本不存在的类和api, 最终项目构建失败。
如其字面意思——属性。properties标签下定性的属性,能够在pom的任何地方被引用, 引用方式为${属性名}
,其中属性名即标签名,咱们能够利用这些属性管理一组依赖, 或者将一些容易变化的值集中到properties标签下管理。
<properties> <org.powermock.version>2.0.2</org.powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-testng</artifactId> <version>${org.powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>${org.powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>${org.powermock.version}</version> <scope>test</scope> </dependency> </dependencies>
除了自定义属性外,maven插件也会提供一些属性,以达到对其进行配置的目的。 同时,maven还提供如下属性能够在pom文件中使用: - env.X : 之前缀env
开头的属性,对应于系统环境变量, 好比经过${env.PATH}
能够取到环境变量PATH
的值。这里须要注意的是, maven对环境变量的大小写是敏感的,windows下的环境变量在maven中必须所有大写。 - project.X : project
为前缀是属性是项目的属性值, 包括了pom中的标签——好比${project.parent.artifactId}能够获取 *<parent>* 下 *<artifactId>* 的值;除此以外还有一些其余属性,具体可参考 [官网说明](http://maven.apache.org/ref/3.6.3/maven-model-builder/#Model_Interpolation)。 值得一提是,该属性值并非取当前pom中的标签,而是取**effective-pom**中的标签, 也就是说能够取到从父pom中继承的一些内容。 - settings.X : 与**project.X**相似,还过**settings.X**取的是**settings.xml**中的内容, 具体内容一样参考 [官网说明](http://maven.apache.org/ref/3.6.3/maven-model-builder/#Model_Interpolation)。 - java系统属性 : 全部能够经过`java.lang.System.getProperties()`方法获取的变量, 都可以在pom中获取,如**${java.home}**。具体可参考 oracle官方文档
在上一章的内容是pom的一些基础标签的使用,灵活使用这些基础标签就能够达成项目的基本构建任务。
除了基本构建外,咱们还每每在构建时须要进行打包前依赖冲突检测、发布前进码检查、 发布时同步发布源码包、自动生成并部署项目站点等任务,maven做为强大的构建工具, 经过构建插件的方式,将全部的这些任务所有沿着maven构建生命周期自动执行。
maven构建的生命周期定义了打包和发布项目时须要经历的流程,maven内置的生命周期有 clean(清理)、default(打包发布)、site(生成站点文档)三种。
这三种生命周期由一些阶段(phase)组成,三种生命周期对应的阶段以下:
Phase | Description |
---|---|
pre-clean | execute processes needed prior to the actual project cleaning |
clean | remove all files generated by the previous build |
post-clean | execute processes needed to finalize the project cleaning |
Phase | Description |
---|---|
validate | validate the project is correct and all necessary information is available. |
initialize | initialize build state, e.g. set properties or create directories. |
generate-sources | generate any source code for inclusion in compilation. |
process-sources | process the source code, for example to filter any values. |
generate-resources | generate resources for inclusion in the package. |
process-resources | copy and process the resources into the destination directory, ready for packaging. |
compile | compile the source code of the project. |
process-classes | post-process the generated files from compilation, for example to do bytecode enhancement on Java classes. |
generate-test-sources | generate any test source code for inclusion in compilation. |
process-test-sources | process the test source code, for example to filter any values. |
generate-test-resources | create resources for testing. |
process-test-resources | copy and process the resources into the test destination directory. |
test-compile | compile the test source code into the test destination directory |
process-test-classes | post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes. |
test | run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed. |
prepare-package | perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package. |
package | take the compiled code and package it in its distributable format, such as a JAR. |
pre-integration-test | perform actions required before integration tests are executed. This may involve things such as setting up the required environment. |
integration-test | process and deploy the package if necessary into an environment where integration tests can be run. |
post-integration-test | perform actions required after integration tests have been executed. This may including cleaning up the environment. |
verify | run any checks to verify the package is valid and meets quality criteria. |
install | install the package into the local repository, for use as a dependency in other projects locally. |
deploy | done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects. |
Phase | Description |
---|---|
pre-site | execute processes needed prior to the actual project site generation |
site | generate the project's site documentation |
post-site | execute processes needed to finalize the site generation, and to prepare for site deployment |
site-deploy | deploy the generated site documentation to the specified web server |
每一个生命周期拥有的阶段互不重叠,且在构建时每一个生命周期中的阶段按顺序进行。 这意味着当咱们使用mvn
命令构建时,若是指定了某一阶段,maven会先执行全部前置阶段, 再执行指定的阶段——这使得maven的使用至关容易,使用都只须要记住经常使用的几个阶段 (如clean、deploy、site)便可单独完成项目的构建和发布。
除了maven内置的阶段外,maven容许插件自定义阶段,好比maven-dependency-plugin 插件中经常使用的copy-dependencies阶段。插件阶段与内置阶段同样可使用mvn
命令运行, 惟一不一样的是须要在阶段前加上插件的坐标信息,如mvn dependency:copy-dependencies
(使用非maven官方插件时,须要将dependency换成groupId:artifactId的形式)。
在使用这些插件前,咱们须要在pom文件中引入插件:
<build> <plugins> <plugin> <artifactId>maven-dependency-plugin</artifactId> <version>2.8</version> </plugin> </plugins> </build>
事实上基本构建插件maven都有内置,super pom中甚至进行了缺乏配置,不须要咱们再手动添加
虽然maven容许插件自定义阶段,但不容许修改内置的生命周期,
build标签下定义了构建的相关信息和配置:
<build> <defaultGoal>install</defaultGoal> <directory>${basedir}/target</directory> <finalName>${artifactId}-${version}</finalName> <resources> <resource> <filtering>true</filtering> <directory>${basedir}/src/main/resources</directory> <includes> <include>**/application*.yml</include> <include>**/application*.yaml</include> <include>**/application*.properties</include> </includes> </resource> <resource> <targetPath>META-INF/plexus</targetPath> <filtering>false</filtering> <directory>${basedir}/src/main/plexus</directory> <includes> <include>configuration.xml</include> </includes> <excludes> <exclude>**/*.properties</exclude> </excludes> </resource> </resources> <testResources> ... </testResources> <filters> <filter>filters/filter1.properties</filter> </filters> </build>
默认构建目标(阶段),若是配置该值,在执行mvn
命令时能够不指定构建阶段。
<defaultGoal>clean package</defaultGoal>
构建生成的相关文件的存放位置,默认配置为${project.basedir}/target
。
构建生成jar包(或其余类型的包)的名称,默认为${artifactId}-${version}
。
须要注意的是,finalName并不是如字面意思通常是打包后的最终名称, 实际打包插件可能会在后面添加后缀:若是咱们配置finalName配置为demo, 同时为打包插件配置classifier标签为jdk17(上面咱们讲到过), 那么实际生成的jar名称为demo-jdk17.jar。
resources标签用来指定资源文件的存放路径,这些文件不会参与编译,会直接拷贝到相应的路径下。
${placeholder}
——这是官方文档的说法,实际在spring boot项目中的有效使用格式为@placeholder@
${project.basedir}/src/main/resources
testResources中的内容与resources一致,不一样的是其对应的生命周期和directory的默认值。
filter能够用来引入一些properties文件,在这样文件里定义的属性, 也会加入到开启了filtering的资源文件的占位符替换范围。
plugins标签用于引入和配置打包插件。
<build> ... <pluginManagement> <plugins> ... </plugins> </pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.6</version> <extensions>false</extensions> <inherited>true</inherited> <configuration> <classifier>test</classifier> </configuration> <dependencies>...</dependencies> <executions>...</executions> </plugin> </plugins> </build>
executions下包含的是execution列表,每一个execution能够理解为一个任务。
<plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.1</version> <executions> <execution> <id>echodir</id> <goals> <goal>run</goal> </goals> <phase>verify</phase> <inherited>false</inherited> <configuration> <tasks> <echo>Build Dir: ${project.build.directory}</echo> </tasks> </configuration> </execution> </executions> </plugin>
mvn help:describe -Dplugin=com.github.spotbugs:spotbugs-maven-plugin:3.1.12.2
pmd、check
关联到verfiy
阶段,这样当咱们运行mvn verify
时, 会自动进行代码问题的检查。pluginManagement的做用与dependencyManagement的做用相似,咱们能够在父pom里配置全部插件, 但不实际引入插件,子pom按需在plugins标签下引入具体插件,这样就能够达到插件配置集中管理的效果 —— 同dependencies下引入dependencyManagement中的依赖同样, plugins下引入pluginManagement中的插件只须要声明groupId与artifactId。
<build> <pluginManagement> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.1</version> <executions> <execution> <id>echodir</id> <goals> <goal>run</goal> </goals> <phase>verify</phase> <inherited>false</inherited> <configuration> <tasks> <echo>Build Dir: ${project.build.directory}</echo> </tasks> </configuration> </execution> </executions> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> </plugin> </plugins> </build>
profiles容许咱们定义多个profile,并设置每一个profile的激活条件,以达到自如切换多套配置的目的。
<profiles> <profile> <id>test</id> <activation>...</activation> <build>...</build> <modules>...</modules> <repositories>...</repositories> <pluginRepositories>...</pluginRepositories> <dependencies>...</dependencies> <reporting>...</reporting> <dependencyManagement>...</dependencyManagement> <distributionManagement>...</distributionManagement> </profile> </profiles>
如字面意思,即套profile的标识,咱们能够经过mvn
命令的-P
选项直接经过id激活对应的profile: mvn -P test clean deploy
profile的自动激活条件。
除了经过手动指定profile外,还能够根据一些条件,自动激活profile。
<profile> <id>test</id> <activation> <activeByDefault>false</activeByDefault> <jdk>1.5</jdk> <os> <name>Windows XP</name> <family>Windows</family> <arch>x86</arch> <version>5.1.2600</version> </os> <property> <name>sparrow-type</name> <value>African</value> </property> <file> <exists>${basedir}/file2.properties</exists> <missing>${basedir}/file1.properties</missing> </file> </activation> </profile>
其中activeByDefault表示是否为默认激活,默认值为false。若是配置为true, 当没有配置文件被激活时,激活该配置文件
其余的标签是一些具体激活条件,在maven3.2.2版本只要知足一项激活条件就成立(或关系), 在maven3.2.2之后必须知足全部项激活条件才成立(与关系): - jdk: 指定构建时的jdk版本,除了指定具体版本外,也能够指定范围 - os: 操做系统信息,具体约束条件内容可查看这里 - property: 将该属性的key-value知足条件时该项成立。这里的属性的范围与pom文件一致。 - file: 文件存在或缺失,配置路径须为绝对路径。该项须要注意的是,file标签下的属性替换, 只容许出现basedir、系统变量、maven命令传递变量这三种。
在咱们配置好profiles,若是想知道在当前环境下哪一个配置将被激活, 能够经过mvn help:active-profiles
查看。
modules、dependencies、dependencyManagement标签咱们在前文中已经讲过, profile下这些标签的可配置内容与其在根标签(project)下的一致, profile下的配置能够覆盖根标签下的配置。
repositories、pluginRepositories、reporting、distributionManagement 这些标签在根标签下也存在,只是咱们尚未聊到,与上面的这些标签同样, 这些标签的可配置内容与其在根标签下的一致,profile下的配置能够覆盖根标签下的配置。
build标签较为特殊,其在profile下的可配置内容只包含前文中咱们提到的全部内容, 但在根标签下的build标签还有一些独占内容咱们没有提到。
在project标签下的build标签下还有几个标签,这些标签仅出如今project标签下的build中, 没法在profile中的内容配置。
这些标签包括目录结构标签——sourceDirectory、sourceDirectory、testSourceDirectory、 outputDirectory、testOutputDirectory,以及extensions标签。
这些标签订义了项目的目录结构,标签的含义和其英文名字一致,标签的值必须为绝对路径 (通常使用`${project.basedir}进行拼接)。
这样须要注意的是除了上述的几个标签外,咱们还能够找到一个scriptSourceDirectory标签, 该标签实际没有用处,已经被官宣废弃。
extensions下能够引入一些jar对maven能力进行扩展,好比引入wagon-ssh添加对ssh协议的支持, 与plugins中的插件不一样,扩展程序不须要提供构建目标(gloas),只是提供能力基础, 所以须要另行引入插件配合使用。
咱们举一个利用ssh协议进行包自动部署的例子:
<build> <extensions> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-ssh</artifactId> <version>2.10</version> </extensions> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>wagon-maven-plugin</artifactId> <version>1.0</version> <configuration> <serverId>devServerRoot</serverId> <formFile>target/demo.jar</formFile> <url>scp://root@x.x.x.x:/root/applications/demo</url> <commands> <command>/root/applications/demo/restart.sh</command> </commands> <desplayCommandOutputs>true</desplayCommandOutputs> </configuration> </plugin> </plugins> </build>
其中serverId中的的devServerRoot于server标签中的id, server标签下能够定义服务器的帐号密码信息,通常在settings.xml中配置,也能够在pom中配置。
<server> <id>devServerRoot</id> <username>root</username> <password>password</password> </server>
reporting下的内容与site
生命周期相关,reporting标签下能够定义一些插件用于生成报告文档, 好比javadoc。
与build相似,reporting也提供了插件的配置能力,与build不一样的是, reporting下的插件没法被绑定到指定的maven阶段(默认绑定site
阶段)
<reporting> <plugins> <excludeDefaults>false</excludeDefaults> <outputDirectory>${basedir}/target/site</outputDirectory> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <version>3.1.1</version> <reportSets> <reportSet> <id>sunlink</id> <reports> <report>javadoc</report> </reports> <inherited>true</inherited> <configuration> <links> <link>http://java.sun.com/j2se/1.5.0/docs/a </link> </links> </configuration> </reportSet> </reportSets> </plugin> </plugins> </reporting>
与build下的plugin相比,reporting下的配置就相对较为简单,只有一个相似于executions 的reportSets标签——二者均定义了插件在某一maven阶段的构建任务。
有一点须要注意的是,一样的插件能够同时出如今build下和reporting下, 虽然咱们能够用pluginManagement统一管理版本, 但build下的相关配置(configuration)不会传递到reporting中