[关闭]
@lemonguge 2017-09-13T14:49:35.000000Z 字数 6712 阅读 373

灵活的构建

Maven


一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建。例如,典型的项目都会有开发环境、测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就需要能够识别所在的环境并使用正确的配置。还有一种常见的情况是,项目开发了大量的集成测试,这些测试运行起来非常耗时,不适合在每次构建项目的时候都运行,因此需要一种手段能让我们在特定的时候才激活这些集成测试。Maven 为了支持构建的灵活性,内置了三大特性,即属性、Profile 和资源过滤。


Maven属性

之前已经介绍过 Maven 属性的使用,最常见的使用 Maven 属性的方式,通过 properties 元素用户可以自定义一个或多个 Maven 属性,然后在 POM 的其他地方使用 ${属性名称} 的方式引用该属性,这种做法的最大意义在于消除重复。不仅减少了日后升级版本的工作量,也能降低错误发生的概率。

Maven 共有 6 类属性,如下:

  1. 内置属性:主要有两个常用内置属性——${basedir} 表示项目根目录,即包含 pom.xml 文件的目录,${version} 表示项目版本。
  2. POM 属性:用户可以使用该类属性引用 POM 文件中对应元素的值。例如 ${project.artifactId} 就对应了 <project><artifactId> 元素的值,常用的 POM 属性可以查看超级 POM 中的定义。
  3. 自定义属性:用户可以在 POM 的 <properties> 元素下自定义 Maven 属性。
  4. Settings 属性:与 POM 属性同理,用户使用以settings.开头的属性引用 settings.xml 文件中 XML 元素的值,如常用的 ${settings.localRepository} 指向用户本地仓库的地址。
  5. Java 系统属性:所有 Java 系统属性都可以使用 Maven 属性引用。
  6. 环境变量属性:所有环境变量都可以使用以 env. 开头的 Maven 属性引用。例如 ${env.JAVA_HOME} 指代了 JAVA_HOME 环境变量的值。

构建环境的差异

在不同的环境中,项目的源码应该使用不同的方式进行构建,最常见的就是数据库配置了。

  1. ## 开发库
  2. database.jdbc.driverClass=com.mysql.jdbc.Driver
  3. database.jdbc.connectionURL=jdbc:mysql://localhost:3306/test
  4. database.jdbc.username=dev
  5. database.jdbc.password=dev-pwd

  1. ## 测试库
  2. database.jdbc.driverClass=com.mysql.jdbc.Driver
  3. database.jdbc.connectionURL=jdbc:mysql://192.168.1.100:3306/test
  4. database.jdbc.username=test
  5. database.jdbc.password=test-pwd

会在 src/main/resources/ 目录下放置 dev、test和prd 文件夹的 jdbc.properties 文件,用于在不同环境的构建。


资源过滤

为了应对环境的变化,首先需要使用 Maven 属性将这些将会发生变化的部分提取出来。在上一节的数据库配置中,连接数据库使用的驱动类、URL、用户名和密码都可能发生变化,因此用 Maven 属性取代它们:

  1. database.jdbc.driverClass=${db.driver}
  2. database.jdbc.connectionURL=${db.url}
  3. database.jdbc.username=${db.username}
  4. database.jdbc.password=${db.password}

这里定义了 4 个 Maven 属性:db.driverdb.urldb.usernamedb.password,它们的命名是任意的,读者可以根据自己的实际情况定义最合适的属性名称。

既然使用了 Maven 属性,就应该在某个地方定义它们,这里要做的是使用一个额外的 profile 将其包裹。

  1. <project>
  2. <profiles>
  3. <profile>
  4. <id>dev</id>
  5. <properties>
  6. <db.driver>com.mysql.jdbc.Driver</db.driver>
  7. <db.url>jdbc:mysql://192.168.1.100:3306/test</db.url>
  8. <db.username>dev</db.username>
  9. <db.password>dev-pwd</db.password>
  10. </properties>
  11. </profile>
  12. </profiles>
  13. </project>

Maven 属性定义与直接在 POM 的 properties 元素下定义并无二致,这里只是使用了一个 iddevprofile,其目的是将开发环境下的配置与其他环境区别开来。

Maven 属性默认只有在 POM 中才会被解析

${db.username} 放到 POM 中会变成 dev,但是如果放到 src/main/resources/ 目录下的文件中,构建的时候它将仍然还是 ${db.username}。因此,需要让 Maven 解析资源文件中的 Maven 属性。

资源文件的处理其实是 maven-resources-plugin 做的事情,它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中。不过只要通过一些简单的 POM 配置,该插件就能够解析资源文件中的 Maven 属性,即开启资源过滤

Maven 默认的主资源目录和测试资源目录的定义是在超级 POM 中。要为资源目录开启过滤,只要在此基础上添加一行 filtering 配置即可:

  1. <project>
  2. <build>
  3. <resources>
  4. <!-- 为主资源目录开启过滤 -->
  5. <resource>
  6. <directory>${project.basedir}/src/main/resources</directory>
  7. <filtering>true</filtering>
  8. </resource>
  9. </resources>
  10. <testResources>
  11. <!-- 为测试资源目录开启过滤 -->
  12. <testResource>
  13. <directory>${project.basedir}/src/test/resources</directory>
  14. <filtering>true</filtering>
  15. </testResource>
  16. </testResources>
  17. </build>
  18. </project>

Maven 允许用户声明多个资源目录,并且为每个资源目录提供不同的过滤配置,这样会破坏 Maven 的约定。

  1. <project>
  2. <build>
  3. <resources>
  4. <resource>
  5. <directory>src/main/resources</directory>
  6. <filtering>true</filtering>
  7. </resource>
  8. <resource>
  9. <directory>src/main/sql</directory>
  10. <filtering>false</filtering>
  11. </resource>
  12. <resources>
  13. </build>
  14. </project>

上面代码配置了两个资源目录,其中 src/main/resources 开启了过滤,而 src/main/sql 没有启用过滤。

到目前为止一切基本就绪了,我们将数据库配置的变化部分提取成了 Maven 属性,在 POM 的 profile 中定义了这些属性的值,并且为资源目录开启了属性过滤。最后只需要在命令行激活 profile,Maven 就能够在构建项目的时候使用 profile 中属性值替换数据库配置文件中的属性引用。

  1. $ mvn clean install -Pdev

mvn-P 参数表示在命令行激活一个 profile。这里激活了 id 为 devprofile。构建完成后,输出目录中的数据库配置就是开发环境的配置了。


资源过滤

Web 项目中还有另外一类资源文件,默认它们的源码位于 src/main/webapp/ 目录,经打包后位于 WAR 包的根目录。

与一般的资源文件一样,web 资源文件默认不会被过滤。开启一般资源文件的过滤也不会影响到 web 资源文件,所以需要配置 maven-war-plugin 对 src/main/webapp/ 这一 web 资源目录开启过滤。

  1. <project>
  2. <build>
  3. <plugins>
  4. <plugin>
  5. <groupId>org.apache.maven.plugins</groupId>
  6. <artifactId>maven-war-plugin</artifactId>
  7. <version>2.1-beta-1</version>
  8. <configuration>
  9. <webResources>
  10. <resource>
  11. <filtering>true</filtering>
  12. <directory>src/main/webapp</directory>
  13. <includes>
  14. <include>**/*.css</include>
  15. <include>**/*.js</include>
  16. </includes>
  17. </resource>
  18. </webResources>
  19. </configuration>
  20. </plugin>
  21. </plugins>
  22. </build>
  23. </project>

Maven Profile

不同环境的profile

为了能让构建在各个环境下方便地移植,Maven 引入了 profile 的概念。profile 能够在构建的时候修改 POM 的一个子集,或者添加额外的配置元素。用户可以使用很多方式激活 profile,以实现构建在不同环境下的移植。

  1. <project>
  2. <profiles>
  3. <profile>
  4. <id>dev</id>
  5. <properties>
  6. <db.driver>com.mysql.jdbc.Driver</db.driver>
  7. <db.url>jdbc:mysql://192.168.1.100:3306/test</db.url>
  8. <db.username>dev</db.username>
  9. <db.password>dev-pwd</db.password>
  10. </properties>
  11. </profile>
  12. <profile>
  13. <id>test</id>
  14. <properties>
  15. <db.driver>com.mysql.jdbc.Driver</db.driver>
  16. <db.url>jdbc:mysql://192.168.1.100:3306/test</db.url>
  17. <db.username>test</db.username>
  18. <db.password>test-pwd</db.password>
  19. </properties>
  20. </profiles>
  21. </profile>
  22. </project>

同样的属性在两个 profile 中的值是不一样的,dev profile 提供了开发环境数据库的配置,而 test profile 提供的是测试环境数据库的配置。类似地,还可以添加一个基于产品环境数据库配置的 profile

可以在使用 mvn 命令的时候在后面加上 -Pdev 激活 dev profile,而测试人员可以使用 -Ptest 激活 test profile

激活profile

为了尽可能方便用户,Maven 支持很多种激活 Profile 的方式。

  1. 命令行激活:使用 mvn 命令行参数 -P 加上 profileid 来激活 profile,多个 id 之间以逗号分隔。例如,使用 mvn clean install -Pdev-x,dev-y 激活了 dev-xdev-y 两个 profile
  2. Settings 文件显式激活:如果用户希望某个 profile 默认一直处于激活状态,就可以配置 settings.xml 文件的 <settings><activeProfiles><activeProfile> 元素,表示其配置的 profile 对于所有项目都处于激活状态。
  3. 系统属性激活:配置当某系统属性存在的时候,自动激活 profile。使用 <profiles><profile><activation><property><name> 配置系统属性存在时激活 profile,也可以进一步配置,在上面的 <profiles><profile><activation><property><value> 指定值,某系统属性存在且值确定时激活 profile。值得注意,用户可以在命令行声明系统属性,mvn clean install -Dtest=x 命令表示为,当某系统属性 test 存在,且值等于 x 的时候激活指定的 profile
  4. 操作系统环境激活:Profile 还可以自动根据操作系统环境激活,如果构建在不同的操作系统有差异,用户完全可以将这些差异写进 profile,然后配置它们自动基于操作系统环境激活。<profiles><profile><activation><os>os 元素下可以有很多系统属性,如 <os><name>Windows XP</name><family>Windows</family><arch>x86</arch><version>5.1.2600</version></os>
  5. 文件存在与否激活:Maven 能够根据项目中某个文件存在与否来决定是否激活 profile。如 <profiles><profile><activation><file>,例如 <file><missing>x.properties</missing><exists>y.properties</exists></file>
  6. 默认激活:用户可以在定义 profile 的时候指定其默认激活。<profiles><profile><activation><activeByDefault>true。需要注意的是,如果 POM 中有任何一个 profile 通过以上其他任意一种方式被激活了,所有的默认激活配置都会失效。

其中很常用的是,命令行激活、Settings 文件显式激活和默认激活。

profile的种类

根据具体的需要,可以在以下位置声明 profile

  1. pom.xml:很显然,pom.xml中声明的 profile 只对当前项目有效。
  2. 用户 settings.xml:用户目录下 .m2/settings.xml 中的 profile 对本机上该用户所有的 Maven 项目有效。
  3. 全局 settings.xml:Maven 安装目录下 conf/settings.xml 中的 profile 对本机上所有的 Maven 项目有效。

通常称 pom.xml 文件声明的 profile 为内部的 profile,其他两种为外部的 profile。这两类的 profile 可以声明的 POM 元素也是不同的。

  1. <!-- pom.xml中的内部profile可使用的元素 -->
  2. <project>
  3. <repositories></repositories>
  4. <pluginRepositories></pluginRepositories>
  5. <distributionManagement></distributionManagement>
  6. <dependencies></dependencies>
  7. <dependencyManagement></dependencyManagement>
  8. <modules></modules>
  9. <properties></properties>
  10. <reporting></reporting>
  11. <build>
  12. <plugins></plugins>
  13. <defaultGoal></defaultGoal>
  14. <resources></resources>
  15. <testResources></testResources>
  16. <finalName></finalName>
  17. </build>
  18. </project>
  19. <!-- 外部profile可使用的元素 -->
  20. <project>
  21. <repositories></repositories>
  22. <pluginRepositories></pluginRepositories>
  23. <properties></properties>
  24. </project>
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注