Introduction to Build Profiles
Apache Maven2.0在确保构建是可移植的方面有了很大的提高。除其他事项外,这也意味着可以在POM中允许构建配置以避免所有的文件系统相关(在继承,依赖和其他方面),同时也加重了学习本地仓库存储元数据知识中使此成为可能部分的难度。
但是,有些时候移植并不完全可能。在某些条件下,plugins可能需要配置本地文件系统路径。在其他环境下,将需要稍微不同的依赖集,并且项目的artifact名称也可能需要轻微的调整。还有在其他时候,你可能依据检测到创建环境在构建生命周期中需要包含一个完整的插件。
为了解决这些情况,Maven 2引进一个叫做构建profile的概念。规定Profiles使用POM中可用元素的一个子集(外加一个额外区域),并且以一种可变化的方式触发。它们在构建时修改POM配置,主要用于指定环境中等价但不相同的参数。例如提供在开发、测试、生产环境下应用服务的根路径。因为如此,使用Profiles可以很容易的在不修改配置情况下达到你想要的结果。如果使用恰当,还可以在要保持项目可移植性时使用profiles。另外,使用profiles还可以简化maven -f选项的使用。
注:-f选项用于允许用户创建另一个POM文件以配置不同的参数和配置来构建项目。
profile定义的位置
- 每个项目:定义在POM文件自身中。
- 每个用户:定义在用户设置中,即${USER_HOME}/.m2/settings.xml.
- 全局配置:定义在全局配置中,即${MAVEN_HOME}/conf/settings.xml.
配置的profile如何被触发,如何确定使用哪个?
Profiles可以通过-P CLI选项显式指定。
此选项使用的参数是一个要使用的由逗号分隔的profile-ids列表,当指定此选项时,此选项中参数所指定的profiles将被激活,除了已经通过激活配置的Profiles或者是在settings.xml中的区域中已配置过的Profiles。
mvn groupId:artifactId:goal -P profile-1,profile-2
Profiles可以在Maven的settings.xml中区域激活,此区域可配置一个元素列表,每个包含一个要激活的profile-id。
<settings>
...
<activeProfiles>
<activeProfile>profile_1</activeProfile>
</activeProfiles>
...
</settings>
Profiles可以通过检测构建环境的状态自动触发。此种触发通过Profile自身的区域指定。现在,这种检测仅限用于JDK版本的检测、系统属情的展示检测和系统属性值的检测,如下是当JDK版本是以1.4开头的时触发Profile配置的例子:
<profiles>
<profile>
<activation>
<jdk>1.4<jdk>
</activation>
...
</profile>
</profiles>
Maven 2.1则可以指定一个范围,如下为匹配JDK 1.3、1.4、1.5:
<profiles>
<profile>
<activation>
<jdk>[1.3,1.6)<jdk>
</activation>
...
</profile>
</profiles>
注:此上的范围绑定比如,1.5],可能不包含许多1.5的发布版本,因为它们会有一个像_05之类的“补丁”,此种情况在上面的绑定范围中是不被包含的。
以下是通过操作系统设置触发Profile的配置:
<profiles>
<profile>
<activation>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
</activation>
...
</profile>
</profiles>
如下是当系统属性debug被指定任意值时触发Profile的配置:
<profiles>
<profile>
<activation>
<property>
<name>debug</name>
</property>
</activation>
...
</profile>
</profiles>
如下是未定义系统属性debug时触发的配置:
<profiles>
<profile>
<activation>
<property>
<name>!debug</name>
</property>
</activation>
...
</profile>
</profiles>
如下是系统属性debug未定义或者定义了值不是true时触发的配置:
<profiles>
<profile>
<activation>
<property>
<name>debug</name>
<value>!true</value>
</property>
</activation>
...
</profile>
</profiles>
如下是系统属性environment值为test时触发的配置:
<profiles>
<profile>
<activation>
<property>
<name>environment</name>
<value>test</value>
</property>
</activation>
...
</profile>
</profiles>
也可以使用如下命令行命令实现上面配置:
mvn groupId:artifactId:goal -Denvironment=test
Maven 3.0中Profile配置也可以基于settings.xml中配置的激活属性被激活。
注:在Windows操作系统上,环境变量名称正常都是全大写的。
如下是生成的target/generated-sources/axistools/wsdl2java/org/apache/maven丢失时触发Profile的配置:
<profiles>
<profile>
<activation>
<file>
<missing>target/generated-sources/axistools/wsdl2java/org/apache/maven</missing>
</file>
</activation>
...
</profile>
</profiles>
如下是使用默认配置激活Profile的配置:
<profiles>
<profile>
<id>profile-1</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
...
</profile>
</profiles>
此时对于所有的构建,Profile配置都会被自动激活,除非同一个POM中的另一个Profile是使用的上面描述的方式激活的。默认激活配置优先级最低,即会被其他配置覆盖。
从Maven 2.0.10开始,如以如下在命令行中解除一个或者多个Profile的激活状态:
mvn groupId:artifactId:goal -P !profile-1,!profile-2
POM中那些区域可以可以通过每种类型的Profile定制
在文件中定义Profile
根据你所配置的Profile,你将能够访问不同的POM配置选项。
Profiles在其他文件中指定(如settings.xml或者profiles.xml)在严格意义上是不可移植的。似乎有很大机会改变构建结果的任何事物都受限于POM中的内联profiles。像仓库列表可能仅仅是一个包含被承认的artifacts的专用仓库,是不会改变构建结果的。因此,你仅可能修改区域和区域,外加一个区域。
在POM中定义Profile
从另一方面讲,如果你的Profile是合理的在POM内部指定,你可以使用更多配置选项。当然,代价就是配置仅影响你的项目和其子模块。因为这些Profile是内部指定的,所以更有可能可移植性保存。可以很公道的说,你可以通过此种配置添加更多的信息,而不会存在信息对其他用户不可用的风险。
Profiles在POM中指定可以修改如下的POM元素:
Profile陷井
我们已经提到添加Profiles可能潜在的破坏你的项目的可移植性,我们甚至已经突出强调有可能破坏项目可移植性的情况,但是,也有必要重申一些进一步关于使用Profiles时避免陷井的要点。
在使用Profiles时有两点要记在心中,第一是外部属性,通常用于插件(plugin)配置,有破坏项目可移植性的风险;第二则是原本不完整的Profiles集指定区域。
外部属性
外部属性定义涉及任何定义在pom.xml之外的任何属性值,但不是定义在对应的Profile中。pom.xml中使用外部属性最明显的就是用于插件配置。例如在settings.xml的Profile中指定应用服务的路径,那么如果项目组中其他人没有类似的settings.xml时,在构建你的integration test就可能因些而失败:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.myco.plugins</groupId>
<artifactId>spiffy-integrationTest-plugin</plugin>
<version>1.0</version>
<configuration>
<appserverHome>${appserver.home}</appserverHome>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
并且在你本地settings.xml(默认为${user.home}/.m2/settings.xml)中有这样的配置:
<settings>
...
<profiles>
<profile>
<id>appserverConfig</id>
<properties>
<appserver.home>/path/to/appserver</appser.home>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>appserverConfig</activeProfile>
</activeProfiles>
...
</settings>
这样的配置你在构建integration-test时是完全没有问题的,但是如果你的队友构建时可能发生两种错误,一是无法解决,二是${appserver.home}对其是不可用的。
原本不完整的Profiles集指定
除了上面所述的破坏可移植性,使用Profiles配置也很难满足所有的场景。当你这样作的时候,通常会使你的目标环境之一搁浅:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.myco.plugins</groupId>
<artifactId>spiffy-integrationTest-plugin</artifactId>
<version>1.0</version>
<configuration>
<appserverHome>${appserver.home></appserverHome>
</configuration>
</plugin>
</plugins>
</build>
...
</project>
然后考虑如下可能于pom.xml中指定的profile配置:
<project>
...
<profiles>
<profile>
<id>appserverConfig-dev-1</id>
<activation>
<property>
<name>env</name>
<value>dev</value>
</property>
</activation>
<properties>
<appserver.home>/path/to/dev/appserver</appserver.home>
</properties>
</profile>
<profile>
<id>appserverConfig-dev-2</id>
<activation>
<property>
<name>env</name>
<value>dev-2</value>
</property>
</activation>
<properties>
<appserver.home>/path/to/another/dev/appserver2</appserver.home>
</properties>
</profile>
</profiles>
...
</project>
如上profile配置直接面向开发环境,新加了一个新的名叫appserverConfig-dev-2的profile配置并且此配置在系统属性包含”env=dev-2”激活使用。如执行:
mvn -Denv=dev-2 integration-test
将使用appserverConfig-dev-2 profile配置构建,当我们执行:
mvn -Denv=dev integration-test
则将使用appserverConfig-dev-1 profile配置构建,但是当执行:
mvn -Denv=production integration-test
则可能构建失败,因为没有对应的profile配置。
这时我们就没有考虑到生产环境对应的profile配置,甚至我们想要使用”test”和 “local”这样的profile配置来构建integration-test的生命周期,这些profile配置构成了原本的目标环境集,如果要配置profile时遗露,我们就会受到固有环境的构建限制。