@lemonguge
2017-09-13T14:49:35.000000Z
字数 6712
阅读 373
Maven
一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建。例如,典型的项目都会有开发环境、测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就需要能够识别所在的环境并使用正确的配置。还有一种常见的情况是,项目开发了大量的集成测试,这些测试运行起来非常耗时,不适合在每次构建项目的时候都运行,因此需要一种手段能让我们在特定的时候才激活这些集成测试。Maven 为了支持构建的灵活性,内置了三大特性,即属性、Profile 和资源过滤。
之前已经介绍过 Maven 属性的使用,最常见的使用 Maven 属性的方式,通过 properties 元素用户可以自定义一个或多个 Maven 属性,然后在 POM 的其他地方使用 ${属性名称} 的方式引用该属性,这种做法的最大意义在于消除重复。不仅减少了日后升级版本的工作量,也能降低错误发生的概率。
Maven 共有 6 类属性,如下:
${basedir} 表示项目根目录,即包含 pom.xml 文件的目录,${version} 表示项目版本。${project.artifactId} 就对应了 <project><artifactId> 元素的值,常用的 POM 属性可以查看超级 POM 中的定义。<properties> 元素下自定义 Maven 属性。${settings.localRepository} 指向用户本地仓库的地址。env. 开头的 Maven 属性引用。例如 ${env.JAVA_HOME} 指代了 JAVA_HOME 环境变量的值。在不同的环境中,项目的源码应该使用不同的方式进行构建,最常见的就是数据库配置了。
## 开发库database.jdbc.driverClass=com.mysql.jdbc.Driverdatabase.jdbc.connectionURL=jdbc:mysql://localhost:3306/testdatabase.jdbc.username=devdatabase.jdbc.password=dev-pwd
## 测试库database.jdbc.driverClass=com.mysql.jdbc.Driverdatabase.jdbc.connectionURL=jdbc:mysql://192.168.1.100:3306/testdatabase.jdbc.username=testdatabase.jdbc.password=test-pwd
会在 src/main/resources/ 目录下放置 dev、test和prd 文件夹的 jdbc.properties 文件,用于在不同环境的构建。
为了应对环境的变化,首先需要使用 Maven 属性将这些将会发生变化的部分提取出来。在上一节的数据库配置中,连接数据库使用的驱动类、URL、用户名和密码都可能发生变化,因此用 Maven 属性取代它们:
database.jdbc.driverClass=${db.driver}database.jdbc.connectionURL=${db.url}database.jdbc.username=${db.username}database.jdbc.password=${db.password}
这里定义了 4 个 Maven 属性:db.driver、db.url、db.username 和 db.password,它们的命名是任意的,读者可以根据自己的实际情况定义最合适的属性名称。
既然使用了 Maven 属性,就应该在某个地方定义它们,这里要做的是使用一个额外的 profile 将其包裹。
<project><profiles><profile><id>dev</id><properties><db.driver>com.mysql.jdbc.Driver</db.driver><db.url>jdbc:mysql://192.168.1.100:3306/test</db.url><db.username>dev</db.username><db.password>dev-pwd</db.password></properties></profile></profiles></project>
Maven 属性定义与直接在 POM 的 properties 元素下定义并无二致,这里只是使用了一个 id 为 dev 的 profile,其目的是将开发环境下的配置与其他环境区别开来。
Maven 属性默认只有在 POM 中才会被解析
${db.username} 放到 POM 中会变成 dev,但是如果放到 src/main/resources/ 目录下的文件中,构建的时候它将仍然还是 ${db.username}。因此,需要让 Maven 解析资源文件中的 Maven 属性。
资源文件的处理其实是 maven-resources-plugin 做的事情,它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中。不过只要通过一些简单的 POM 配置,该插件就能够解析资源文件中的 Maven 属性,即开启资源过滤。
Maven 默认的主资源目录和测试资源目录的定义是在超级 POM 中。要为资源目录开启过滤,只要在此基础上添加一行 filtering 配置即可:
<project><build><resources><!-- 为主资源目录开启过滤 --><resource><directory>${project.basedir}/src/main/resources</directory><filtering>true</filtering></resource></resources><testResources><!-- 为测试资源目录开启过滤 --><testResource><directory>${project.basedir}/src/test/resources</directory><filtering>true</filtering></testResource></testResources></build></project>
Maven 允许用户声明多个资源目录,并且为每个资源目录提供不同的过滤配置,这样会破坏 Maven 的约定。
<project><build><resources><resource><directory>src/main/resources</directory><filtering>true</filtering></resource><resource><directory>src/main/sql</directory><filtering>false</filtering></resource><resources></build></project>
上面代码配置了两个资源目录,其中 src/main/resources 开启了过滤,而 src/main/sql 没有启用过滤。
到目前为止一切基本就绪了,我们将数据库配置的变化部分提取成了 Maven 属性,在 POM 的 profile 中定义了这些属性的值,并且为资源目录开启了属性过滤。最后只需要在命令行激活 profile,Maven 就能够在构建项目的时候使用 profile 中属性值替换数据库配置文件中的属性引用。
$ mvn clean install -Pdev
mvn 的 -P 参数表示在命令行激活一个 profile。这里激活了 id 为 dev的 profile。构建完成后,输出目录中的数据库配置就是开发环境的配置了。
Web 项目中还有另外一类资源文件,默认它们的源码位于 src/main/webapp/ 目录,经打包后位于 WAR 包的根目录。
与一般的资源文件一样,web 资源文件默认不会被过滤。开启一般资源文件的过滤也不会影响到 web 资源文件,所以需要配置 maven-war-plugin 对 src/main/webapp/ 这一 web 资源目录开启过滤。
<project><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.1-beta-1</version><configuration><webResources><resource><filtering>true</filtering><directory>src/main/webapp</directory><includes><include>**/*.css</include><include>**/*.js</include></includes></resource></webResources></configuration></plugin></plugins></build></project>
为了能让构建在各个环境下方便地移植,Maven 引入了 profile 的概念。profile 能够在构建的时候修改 POM 的一个子集,或者添加额外的配置元素。用户可以使用很多方式激活 profile,以实现构建在不同环境下的移植。
<project><profiles><profile><id>dev</id><properties><db.driver>com.mysql.jdbc.Driver</db.driver><db.url>jdbc:mysql://192.168.1.100:3306/test</db.url><db.username>dev</db.username><db.password>dev-pwd</db.password></properties></profile><profile><id>test</id><properties><db.driver>com.mysql.jdbc.Driver</db.driver><db.url>jdbc:mysql://192.168.1.100:3306/test</db.url><db.username>test</db.username><db.password>test-pwd</db.password></properties></profiles></profile></project>
同样的属性在两个 profile 中的值是不一样的,dev profile 提供了开发环境数据库的配置,而 test profile 提供的是测试环境数据库的配置。类似地,还可以添加一个基于产品环境数据库配置的 profile。
可以在使用 mvn 命令的时候在后面加上 -Pdev 激活 dev profile,而测试人员可以使用 -Ptest 激活 test profile。
为了尽可能方便用户,Maven 支持很多种激活 Profile 的方式。
mvn 命令行参数 -P 加上 profile 的 id 来激活 profile,多个 id 之间以逗号分隔。例如,使用 mvn clean install -Pdev-x,dev-y 激活了 dev-x 和 dev-y 两个 profile。profile 默认一直处于激活状态,就可以配置 settings.xml 文件的 <settings><activeProfiles><activeProfile> 元素,表示其配置的 profile 对于所有项目都处于激活状态。profile。使用 <profiles><profile><activation><property><name> 配置系统属性存在时激活 profile,也可以进一步配置,在上面的 <profiles><profile><activation><property><value> 指定值,某系统属性存在且值确定时激活 profile。值得注意,用户可以在命令行声明系统属性,mvn clean install -Dtest=x 命令表示为,当某系统属性 test 存在,且值等于 x 的时候激活指定的 profile。profile,然后配置它们自动基于操作系统环境激活。<profiles><profile><activation><os>,os 元素下可以有很多系统属性,如 <os><name>Windows XP</name><family>Windows</family><arch>x86</arch><version>5.1.2600</version></os>。profile。如 <profiles><profile><activation><file>,例如 <file><missing>x.properties</missing><exists>y.properties</exists></file>。profile 的时候指定其默认激活。<profiles><profile><activation><activeByDefault>true。需要注意的是,如果 POM 中有任何一个 profile 通过以上其他任意一种方式被激活了,所有的默认激活配置都会失效。其中很常用的是,命令行激活、Settings 文件显式激活和默认激活。
根据具体的需要,可以在以下位置声明 profile:
profile 只对当前项目有效。profile 对本机上该用户所有的 Maven 项目有效。profile 对本机上所有的 Maven 项目有效。通常称 pom.xml 文件声明的 profile 为内部的 profile,其他两种为外部的 profile。这两类的 profile 可以声明的 POM 元素也是不同的。
<!-- pom.xml中的内部profile可使用的元素 --><project><repositories></repositories><pluginRepositories></pluginRepositories><distributionManagement></distributionManagement><dependencies></dependencies><dependencyManagement></dependencyManagement><modules></modules><properties></properties><reporting></reporting><build><plugins></plugins><defaultGoal></defaultGoal><resources></resources><testResources></testResources><finalName></finalName></build></project><!-- 外部profile可使用的元素 --><project><repositories></repositories><pluginRepositories></pluginRepositories><properties></properties></project>