Maven

Published: by Creative Commons Licence

  • Tags:

Maven

modules

modules用于定义模块,一个项目可以切分为多个子模块。每个子模块都有自己的pom文件。

<project>
	<modules>
		<module>api</module>
		<module>service</module>
		<module>dao</module>
	</modules>
</project>

利用mvn构建项目时,会同时构建其所有子模块。

parent

一个pom可以设置另外一个pom为父pom,这样子pom会继承父pom的所有内容。

<project> 
	<parent>
		<groupId>com.daltao</groupId>
		<artifactId>everything-parent</artifactId>
		<version>1.0-SNAPSHOT</version>
	</parent>
</project>

dependencyManagement

由于在父pom中定义依赖,会导致子pom必定拥有该依赖。为了消除不必要的依赖但是同时拥有继承的好处(比如关联依赖的版本一致),maven中提供了dependancyManagement元素。只有在显示地增加dependence时,才会利用到dependancyManagement中声明的值作为默认值。

<project>
	<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>${gson.version}</version>
            </dependency>
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-api</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
	</dependencyManagement>
</project>

import

dependancyManagement中的依赖的scope可以设置为import,表示继承该pom中的dependancyManagement内容。

<project>
	<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>${gson.version}</version>
				<type>pom</type>
				<scope>import</scope>
            </dependency>
        </dependencies>
	</dependencyManagement>
</project>

scope

scope用于指定依赖的作用范围,类似于Java中注解的Retention。其可选值如下:

  • compile: 默认,表示依赖将会在编译、运行时使用。
  • provided: 表示依赖仅用于编译时。
  • runtime: 表示依赖仅用于运行时(通过反射使用)
  • test: 表示依赖仅用于测试用例的编译和运行

properties

properties元素允许用户定义多个属性,而在其他地方可以利用${propertyName}来引用。

<project>
	<properties>
    	<gson.version>2.8.5</gson.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>${gson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

maven除了自定义属性,还允许我们访问外部属性:

  • Settings属性:用户可以利用settings开头的属性引用settings.xml文件中XML元素的值,比如${settings.localRepository}。
  • Java系统属性:所有java系统属性都可以直接引用,例如${user.home}指向了用户目录。
  • env环境变量属性:所有环境变量都可以通过以env开头的Maven属性引用,例如${env.JAVA_HOME}。

maven也有一系列的内置属性: 预定义属性:

  • ${basedir}表示项目根目录,即包含pom.xml文件的目录;
  • ${version}表示项目版本;
  • ${project.basedir}同${basedir};
  • ${project.baseUri}表示项目文件地址;
  • ${maven.build.timestamp}表示项目构件开始时间;
  • ${maven.build.timestamp.format}表示属性${maven.build.timestamp}的展示格式,默认值为yyyyMMdd-HHmm,可自定义其格式,其类型可参考Java.text.SimpleDateFormat

POM属性:

  • ${project.build.directory}表示主源码路径,缺省为target;
  • ${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes
  • ${project.build.sourceEncoding}表示主源码的编码格式;
  • ${project.build.sourceDirectory}表示主源码路径;
  • ${project.build.finalName}表示输出文件名称,缺省为${project.artifactId}-${project.version};
  • ${project.packaging} 打包类型,缺省为jar;
  • ${project.version}表示项目版本,与${version}相同;

build

filters

filters标签中可以指定filter,filter则指定一个文件,文件为包含属性值的properties文件,相当于从中导入properties。

<project>
	<build>
        <filters>
          <filter>src/main/filters/${filter.file}.properties</filter>
        </filters>
    </build>
</project>

profiles

profiles中可以指定profile标签,每个profile都指定了一套环境。

<project>
	<profile>
    	<id>dev</id>
        <properties>
        	<filter.file>dev.properties</filter.file>
        </properties>
    </profile>
</project>

在使用mvn命令的时候,可以利用-Pdev指定使用的profile为dev

resources

resources标签用于为mave-resources-plugin提供资源文件信息,mave-resources-plugin会在process-sources和process-test-resources阶段拷贝资源文件到编译目录中。

<project>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>context.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources2</directory>
                <excludes>
                    <exclude>context.xml</exclude>
                </excludes>
            </resource>
        </resources>
    </build>
</project>

替换资源文件中的占位符

profile中一般指定不同环境下的配置信息,但是这些配置信息需要写入到资源文件中才能生效。但是pom中的属性仅在pom中可以访问。因此我们需要借助插件在特定生命周期阶段做资源文件占位符替换的工作。

Maven-resources-plugin为我们做了这些工作,它默认行为是将项目资源文件复制到代码编译输出目录中,只需要通过下面配置,就可以让插件解析资源文件中的maven属性。

<project>
	<build>
    	<resources>
        	<directory>${project.basedir}/src/main</directory>
            <filtering>true</filtering> <!--开启Maven属性替换-->
        </resources>
    </build>
</project>

plugin

生命周期

Maven有三套独立的生命周期:

  • clean
  • default
  • site

clean的生命周期目的是清理项目,default生命周期的目的是构建项目,site的生命周期的目的是建立项目站点。

每个生命周期由一系列生命周期阶段组成,这些生命周期的阶段可以被用户单独调用。如果用户显式调用某个生命周期,这意味着该生命周期和其前置操作都会被执行,但是后续操作将被跳过。

mvn clean
mvn package
mvn install

clean生命周期

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

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

default生命周期

default生命周期定义了真正构建时需要执行的所有步骤,其包含下面阶段:

  1. validate
  2. initialize
  3. generate-sources
  4. process-sources, 处理项目资源文件,一般是复制操作
  5. generaet-resources
  6. compile,编译项目的源代码,一般时编译java文件并将class文件输出到classpath中
  7. process-classes
  8. generate-test-sources
  9. process-test-sources,处理项目测试资源文件,一般是复制操作
  10. process-test-resources
  11. test-compile,编译项目中的测试代码
  12. process-test-classes
  13. test,使用单元测试框架执行测试
  14. prepare-package
  15. package,接受编译好的代码,并打包成可发布的格式
  16. pre-integration-test
  17. integration-test
  18. post-integeration-test
  19. verify
  20. install,将包安装到maven本地仓库
  21. deploy,将最终的包复制到远程仓库

site生命周期

site生命周期的目的是建立和发布项目站点

  1. pre-site,执行一些在生成项目站点之前需要完成的工作
  2. site,生成项目站点文档
  3. post-site,执行一些在生成项目站点之后需要完成的工作
  4. site-deploy,将生成的项目站点发布到服务器上

插件目标

Maven仅仅定义了抽象的生命周期,具体的任务是交给插件完成,插件以独立的构件形式存在。

对于插件本身,为了能够复用代码,它往往能够完成多个任务。因为这些任务背后有大量的可复用代码,因此,这些功能聚集在一个插件中,每个功能对应一个插件目标。

插件目标一般通过pluginPrefix:goal指定,比如compiler:compilesurefire:test

插件的目标绑定在生命周期阶段上,以完成某个具体的构建任务。

内置绑定

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

插件目标 生命周期 作用
maven-clean-plugin:clean clean 删除项目的输出目录
maven-site-plugin:site site  
maven-site-plugin:deploy site-deploy  
maven-resources-plugin:resources process-resources 复制主资源文件至主输出目录
maven-compiler-plugin:compile compile 编译主资源文件至输出目录
maven-resources-plugin:testResources process-test-resources 复制测试资源文件至测试输出目录
maven-compiler-plugin:testCompile test-compile 编译测试代码至测试输出目录
maven-surefire-plugin:test test 执行测试用例
maven-jar-plugin:jar package 构建项目jar包
maven-install-plugin:install install 将项目输出构件安装到本地仓库
maven-deploy-plugin:deploy deploy 将项目输出构建部署到远程仓库

自定义绑定

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

    <project>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>3.0.1</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </project>

executions下每个execution子元素可以用来配置执行一个任务,其将jar-no-fork绑定到verify生命周期上。

在上面情形下即使不指定<phase>也可以正常打包,因为很多插件的目标在编写的时候就已经定义了默认的绑定阶段,可以利用maven-help-plugin查看插件信息mvn help:describe -Dplugin=org.apach.maven-source-plugin:3.0.1 -Ddetail

如果同时多于一个插件目标绑定在某个生命周期上,那么插件目标的执行顺序由其声明的先后顺序所决定。

插件参数

如果能在命令行执行mvn命令的时候改变某些插件的行为,无疑非常方便。用户可以在maven命令中使用-D参数名=参数值的形式提供参数。例如maven-surefire-plugin提供了一个maven.test.skip参数用于跳过测试。

之所以使用-D是因为Java中使用-D传递系统属性,maven重用了它。

如果参数一般都是固定的,那么我们可以直接在pom文件中对参数进行配置。

<project>
	<plugins>
    	<plugin>
        	<.../>
            <configuration>
            	<paramName1>paramValue1</paramName1>
                <paramName2>paramValue2</paramName2>
                ...
            </configuration>
            <.../>
        </plugin>
    </plugins>
</project>

直接调用插件目标

mvn可以直接调用插件目标

mvn [options] <goals> <phases>

仓库

maven中任何一个依赖,插件或者项目构建的输出都称为构件。而存储构件的地方称为仓库。任何构件都有唯一的坐标,定义了它在仓库中的唯一存储路径。

Maven仓库分为两类:

  1. 本地仓库
  2. 远程仓库

而远程仓库还分为:

  1. 中央仓库
  2. 私服
  3. 其他公共库

本地仓库

本地仓库顾名思义就是Maven在本地存储构件的地方,可以认为是远程仓库的缓存。本地仓库的默认地址是~/.m2/repository,允许在maven的settings.xml中进行配置:

<settings>
	<localRepository>/var/maven/repository</localRepository>
</settings>

中央仓库

而中央仓库是默认的远程仓库,maven在安装的时候就自带了中央仓库的配置(所有的maven项目都继承自超级pom)。中央仓库包含了绝大多数流行的开源java构件。

<repositories>
	<repository>
		<id>central</id>
		<name>Central Repository</name>
 		<url>http://repo.maven.apache.org/maven2</url>
		<layout>default</layout>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

私服

私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服同时能代理广域网上的远程仓库,供局域网内的Maven用户使用。当Maven需要下载构件的时候,它从私服请求,如果私服上不存在该构件,则从外部的远程仓库下载,并缓存在私服之上。同时我们还可以把一些无法从外部仓库下载到的构件上传到私服上。

主流的maven私服有:

  1. Apache的Archiva
  2. JFrog的Artifactory
  3. Sonatype的Nexus

远程仓库配置

要增加新的远程仓库可以在settings中增加/settings/repositories/repository

<repositories>
	<repository>
		<id>jboss</id>
		<name>JBoss Repository</name>
		<url>http://repository.jboss.com/maven2/</url>
		<releases>
			<updatePolicy>daily</updatePolicy><!-- never,always,interval n -->
			<enabled>true</enabled>
			<checksumPolicy>warn</checksumPolicy><!-- fail,ignore -->
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
		<layout>default</layout>
	</repository>
</repositories>

如果远程仓库需要提供账号信息,则可以将认证信息配置到settings.xml的/settings/servers/server中:

<servers>
    <server>
		<id>same with repository id in pom</id>
		<username>username</username>
		<password>pwd</password>
	</server>
</servers>

部署构件到远程仓库

为了能将生成的项目部署到远程仓库,需要在pom中配置/settings/distributionManagement:

<settings>
	<dstributionManagement>
    	<repository> <!--发行版本仓库-->
            <id>jboss</id>
			<name>JBoss Repository</name>
			<url>http://repository.jboss.com/maven2/</url>
        </repository>
        <snapshotRepository> <!--快照版本仓库-->
        	<id>jboss</id>
			<name>JBoss Repository</name>
			<url>http://repository.jboss.com/maven2/</url>
        </snapshotRepository>
    </dstributionManagement>
</settings>

镜像

镜像是指内容的完全拷贝,取自镜子成像的含义。在Maven中一个仓库的镜像表示的是提供与该仓库相同服务的站点。

对于某个特定的仓库,你可能希望使用一个替换的镜像而不更改pom文件。

编译settings.xml,设置正确的id,name和url,并将mirrorOf设置为仓库id。

<settings>
  ...
  <mirrors>
    <mirror>
      <id>UK</id>
      <name>UK Central</name>
      <url>http://uk.maven.org/maven2</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

注意一个repository最多只能有一个镜像,换言之不同的镜像的mirrorOf字段不能取相同值(如果多个有相同值,maven会直接取第一个)。

mirrorOf字段可以取下列值:

  • *=匹配一切仓库
  • repo1,repo2=repo1或repo2
  • *,!repo1=所有仓库除了repo1