灵活的构建
一个优秀的构建系统必须足够的灵活,它应该能够让项目在不同的环境下都能成功地构建。Maven 为了支持构建的灵活性,内置了三大特性,即属性、Profile和资源过滤。
-
Maven 属性
Maven 有6类属性,我们可以在 POM 中通过 ${属性名称} 的方式引用该属性:
-
内置属性:主要有两个常用内置属性
- ${dasedir}:项目根目录
- ${version}:表示项目版本
-
POM 属性:我们可以使用该类属性引用 POM 文件中对应元素的值
- ${project.build.sourceDirectory}:项目的主源码目录,默认为 src/main/java/
- ${project.build.testSourceDirectory}:项目的测试源码目录,默认为 src/test/java/
- ${project.build.directory}:项目构建输出目录,默认为 target/
- ${project.outputDirectory}:项目主代码编译输出目录,默认为 target/classes/
- ${project.testOutputDirectory}:项目测试代码编译输出目录,默认为 :target/test-classes
- ${project.groupId}:项目的 groupId
- ${project.artifactId}:项目的 artifactId
- ${project.version}:项目的 version,与 ${version} 等价
- ${project.build.finalName}:项目打包输出文件的名称,默认为 p r o j e c t . a r t i f a c t I d − {project.artifactId}- project.artifactId−{project.version}
-
自定义属性:我们可以在 POM 的 元素下自定义 Maven 属性。例如:
<project>
...
<properties>
<my.prop>haha</my.prop>
</properties>
...
</project>
然后在 POM 中其它地方使用 ${my.prop} 的时候会被替换成 haha。
-
Settings 属性:与 POM 属性同理,我们可以以 settings. 开头的属性引用 settings.xml 文件中XML 元素的值,如常用的 ${settings.localRepository} 指向我们本地仓库的地址。
-
Java 系统属性:所有 Java 系统属性都可以使用 Maven 属性引用。例如 ${user.home} 指向了用户目录。可以使用 mvn help:system 查看所有的 Java 系统属性。
-
环境变量属性:所有环境变量都可以使用以 env. 开头的 Maven 属性引用。例如 ${env.JAVA_HOME} 指代了 JAVA_HOME 环境变量的值。可以使用 mvn help:system 查看所有的环境变量。
-
构建环境的差异
在不同的环境中,项目的源码应该使用不同的方式进行构建,最常见的就是数据库的配置。例如在开发过程中,有些项目会在 src/main/resource/ 目录下放置带有如下内容的数据库配置文件:
database.jdbc.driverClass = com.mysql.jdbc.Driver database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev database.jdbc.username = dev database.jdbc.password = dev-pwd
而在测试环境下,以上配置可能又会是这样的:
database.jdbc.driverClass = com.mysql.jdbc.Driver database.jdbc.connectionURL = jdbc:mysql://192.168.0.189:3306/test database.jdbc.username = test database.jdbc.password = test-pwd
对于以上问题 Maven 给出的解决方案是针对不同的环境生成不同的构件。也就是说,在构建项目的过程中 Maven 就已经将这种差异处理好了。
-
资源过滤
为了应对环境的变化,首先需要使用 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 属性,就应该在某个地方定义它们。在 POM 配置如下:
<profiles> <profile> <id>dev</id> <properties> <db.driver>com.mysql.jdbc.Driver</db.driver> <db.url>jdbc:mysql://localhost:3306/dev</db.url> <db.username>dev</db.username> <db.password>dev-pwd</db.password> </properties> </profile> </profiles>
profile 元素下定义与直接在 POM 的 properties 元素下定义并无二致,这里只是使用了一个Id为 dev 的 profile,其目的是将开发环境下的配置与其它环境区分开来。
Maven 属性默认只有在 POM 中才会被解析。也就是说 ${db.username} 放到 POM 中会变成 test,但是如果放到 src/main/resources/ 目录下的文件中,构建的时候它将仍然还是 ${db.username}。因此需要让 Maven 解析资源文件中的 Maven 属性。
资源文件的处理其实是 maven-resources-plugin 做的事情,它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中。不过只要通过一些简单的 POM 配置,该插件就能够解析资源文件中的 Maven 属性,即开启资源过滤。Maven 默认的主资源目录和测试资源目录的定义是在超级 POM 中。要为资源目录开启过滤,只要在此基础上添加一行 filtering 配置即可,配置如下:
<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>
从上述配置中可以看出,主资源目录和测试资源目录都可以超过一个,虽然会破坏 maven 的约定,但 Maven 允许声明多个资源目录,并且为每个资源目录提供不同的过滤配置,如下:
<resources> <resource> <directory>${project.basedir}/src/main/resources</directory> <filtering>true</filtering> </resource> <resource> <directory>${project.basedir}/src/main/sql</directory> <filtering>false</filtering> </resource> </resources>
到此我们已经将数据库配置的变化部分提取成了 Maven 属性,在 POM 的 profile 中定义了这些属性的值,并且为资源目录开启了属性过滤。最后,只需要在命令行激活 profile,Maven 就能够在构建项目的时候使用 prifile 中属性值替换数据库配置文件中的属性引用。命令如下:
$ mvn clean install -Pdev
mvn 的 -P 参数表示在命令行激活一个 profile。这里激活了 ID 为 dev 的 profile。
-
Maven Profile
要想使得一个构建不做任何修改就能在任何环境下运行,往往是不可能的。为了能让构建在各个环境下方便地移植,Maven 引入了 profile 的概念。profile 能够在构建的时候修改 POM 的一个子集,或者添加额外的配置元素。我们可以使用很多方式激活 profile,以实现构建在不同环境下的移植。
profile 配置如下:
<profiles> <profile> <id>dev</id> <properties> <db.driver>com.mysql.jdbc.Driver</db.driver> <db.url>jdbc:mysql://localhost:3306/dev</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://localhost:3306/test</db.url> <db.username>test</db.username> <db.password>test-pwd</db.password> </properties> </profile> </profiles>
为了尽可能满足用户,Maven 支持很多种激活 profile 的方式。
-
命令行激活:用户可以使用 mvn 命令行参数 -P 加上profile 的id 来激活 profile,多个 id 之间以逗号分隔。例如下面命令激活了 dev-x 和 dev-y 两个 profile:
$ mvn clean install -Pdev-x,dev-y
-
settings 文件显式激活:如果用户希望某个 profile 默认一直处于激活状态,就可以配置 settings.xml 文件的 activeProfiles 元素,表示其配置的 profile 对于所有项目都处于激活状态,配置如下:
<settings> <activeProfiles> <activeProfile>dev-x</activeProfile> </activeProfiles> ... </settings>
-
系统属性激活:用户可以配置当某系统属性存在的时候,自动激活 profile,配置如下:
<profiles> <profile> <activation> <name>test</name> </activation> </profile> ... </profiles>
还可以进一步配置当系统属性 test 存在,且值等于 x 的时候激活 profile,配置如下:
<profiles> <profile> <activation> <name>test</name> <value>x</value> </activation> </profile> ... </profiles>
不要忘了,用户可以在命令行声明系统属性。例如:
$ mvn clean install -Dtest=x
因此,这其实也是一种从命令行激活 profile 的方法,而且多个 profile 完全可以使用同一个系统属性来激活。
-
操作系统环境激活:profile 还可以自动根据操作系统环境激活,配置如下:
<profiles> <profile> <activation> <os> <name>Windows XP</name> <famliy>Windows</famliy> <arch>x86</arch> <version>5.1.2600</version> </os> </activation> </profile> ... </profiles>
这里的 famliy 的值包括 Windows、UNIX和 Mac 等,而其它几项 name、arch、version,可以通过查看环境中的系统属性 os.name、os.arch、os.version 获得。
-
文件存在与否激活:根据项目中某个文件存在与否决定是否激活 profile,配置如下:
<profiles> <profile> <activation> <file> <missing>x.properties</missing> <exists>y.properties</exists> </file> </activation> </profile> ... </profiles>
-
默认激活:可以在定义 profile 的时候指定其默认激活,配置如下:
<profiles> <profile> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> ... </profiles>
使用 activeByDefault 元素可以指定 profile 自动激活。不过需要注意的是,如果 POM 中有任何一个通过以上其它的任意一种方式被激活了,所有的默认配置都会失效。
maven-help-plugin 提供了一个目标帮助我们了解当前激活的 profile:$ mvn help:active-profiles
maven-help-plugin 还有另外一个目标来列出所有 profile:
$ mvn help:all-profiles
profile 的种类 根据具体需要,可以在以下位置声明 profile:
-
pom.xml:很显然,pom.xml中声明的 profile 只对当前项目有效。
-
用户 settings.xml:用户目录下 .m2/settings.xml 中的 profile 对本机上该用户的所有 Maven 项目有效。
-
全局 settings.xml:Maven 安装目录下 conf/settings.xml 中的 profile 对本机上所有 Maven 项目有效。
-
profile.xml(Maven 2):还可以在项目根目录下使用一个额外的 profile.xml 文件来声明 profile,不过该特性已经在 Maven3 中被移除。建议将这类 profile 移到 settings.xml 中。
不同类型的 profile 中可以声明的 POM 元素也是不同的,pom.xlm 中的 profile 能随着 pom.xml 一起被提交到代码仓库中、被 Maven 安装到本地仓库中、被部署到远程 Maven 仓库中。换言之,可以保证该 profile 伴随着某个特定的 pom.xml 一起存在,因此它可以修改或者增加很多 POM 元素,如下:
<profile> <repositories></repositories> <pluginRepositories></pluginRepositories> <distrbutionManagement></distrbutionManagement> <dependencies></dependencies> <dependencyManagement></dependencyManagement> <modules></modules> <properties></properties> <reporting></reporting> <build> <plugins></plugins> <defaultGoal></defaultGoal> <resources></resources> <testResources></testResources> <finalName></finalName> </build> </profile>
在 pom.xml 外部的 profile 只能够声明如下 几个元素:
<profile> <repositories></repositories> <pluginRepositories></pluginRepositories> <properties></properties> </profile>
-
WEB 资源过滤
Web 项目中有另外一类资源文件,默认它们的源码位于 src/main/webapp/目录,经打包后位于WAR包的根目录,例如css源文件,这一类资源文件称做 web 资源文件,它们在打包过后不位于应用程序的 classpath 中。与一般资源文件一样,web 资源文件默认不会被过滤。开启一般资源文件的过滤也不会影响到 web 资源文件。但可以配置 maven-war-plugin 对 src/main/webapp/ 这一 web 资源目录开启过滤,配置如下:
<plugin> <groupId>org.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>