关于灵活的构建
一个优秀的构建系统必须足够灵活,它应该能够让项目在不同的环境下都能成功地构建。
例如,典型的项目都会有开发环境、测试环境和产品环境,这些环境的数据库配置不尽相同,那么项目构建的时候就需要能够识别所在的环境并使用正确的配置
还有一种常见的情况是,项目开发了大量的集成测试,这些测试运行起来非常耗时,不适合在每次构建项目的时候都运行,因此需要一种手段能让我们在特定的时候才激活这些集成测试,Maven为了支持构建的灵活性,内置了三大特性,即属性、Profile和资源过滤
Maven属性
使用Maven属性归类依赖
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
...
</dependencies>
这可能是最常见的使用Maven属性的方式,通过<properties>元素用户可以自定义一个或多个Maven属性,然后再POM的其他地方使用${属性名称}的方式引用该属性,这种做法的最大意义在于消除重复
Maven的6类属性
1、内置属性,主要有两个常用内置属性——${basedir} 表示项目根目录,即包含pom.xml文件的目录;${version} 表示项目版本
2、POM属性,用户可以使用该类属性引用POM文件中对应元素的值,例如:
${project.artifactId}就对应了<project> <artifactId>元素的值,常用的POM属性包括:
n ${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/
n ${project.build.testSourceDirectory}:项目的测试源码目录,默认为src/test/java/
n ${project.build.directory}:项目构建输出目录,默认为target/
n ${project.outputDirectory}:项目主代码编译输出目录,默认为target/classes/
n ${project.testOutputDirectory}:项目测试代码编译输出目录,默认为target/testclasses/
n ${project.groupId}:项目的groupId
n ${project.artifactId}:项目的artifactId
n ${project.version}:项目的version,与${version}等价
n ${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-
${project.version}
这些属性都对应了一个POM元素,它们中一些属性的默认值都是在超级POM中定义的
3、自定义属性,用户可以在POM的<properties>元素下自定义Maven属性
<project>
...
<properties>
<my.prop>hello</my.prop>
</properties>
...
</project>
然后在POM中其他地方使用${my.prop}的时候会被替换成hello
4、Settings属性,与POM属性同理,用户使用以settings.开头的属性引用settings.xml文件中xml元素的值,如常用的${settings.localRepository}指向用户本地仓库的地址
5、Java系统属性,所有Java系统属性都可以使用Maven属性引用,例如${user.home}指向了用户目录,用户可以使用mvn help:system查看所有的Java系统属性
6、环境变量属性,所有环境变量都可以使用以env.开头的Maven属性引用。例如${env.JAVA_HOME}指代了JAVA_HOME环境变量的值。用户可以使用mvn help:system查看所有的环境变量
使用POM属性配置依赖
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>account-email</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>account-persist</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
使用Maven属性配置插件
修改测试报告目录
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<reportsDirectory>
${project.build.directory}/test-reports
</reportsDirectory>
</configuration>
</plugin>
构建环境的差异,考虑一下这种情况
在不同的环境中,项目的源码应该使用不同的方式进行构建,最常见的就是数据库配置了
例如在开发的过程中,有些项目会在src/main/resources/目录下放置带有如下内容的数据库配置文件:
database.jdbc.driverClass=com.mysql.jdbc.Driver
database.jdbc.connectionURL=jdbc:mysql://localhost:3306/test
database.jdbc.username=dev
database.jdbc.password=dev-pwd
这本没什么问题,可当测试人员想要构建项目产品并进行测试的时候,他们往往需要使用不同的数据库,这时的数据库配置文件可能是这样的:
database.jdbc.driverClass=com.mysql.jdbc.Driver
database.jdbc.connectionURL=jdbc:mysql://10.1.0.56:3306/test
database.jdbc.username=test
database.jdbc.password=test-pwd
连接数据库的URL、用户名和密码都发生了变化,类似地,当项目被发布到产品环境的时候,所使用的数据库配置又是另外一套了。这个时候,比较原始的做法是,使用与开发环境一样的构建,然后再测试或者发布产品之前再手动更改这些配置。这是可行的,也是比较常见的,但肯定不是最好的方法。本书已经不止一次强调,手动往往就意味着低效和错误,因此需要找到一种方法,使它能够自动对构建环境的差异
Maven的答案是针对不同的环境生成不同的构件。也就是说,在构建项目的过程中,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
使用一个额外的profile包裹自定义Maven属性
针对开发环境的数据库配置
<profiles>
<profile>
<id>dev</id>
<properties>
<db.driver> com.mysql.jdbc.Driver </db.driver>
<db.url> jdbc:mysql://localhost:3306/test </db.url>
<db.username> dev </db.username>
<db.password> dev-pwd </db.password>
</properties>
</profile>
</profiles>
对上面这些配置进行解释
Maven属性定义与直接在POM的properties元素下定义没什么区别,只是使用了一个id为dev的profile,其目的是将开发环境下的配置与其他环境区分开
那么,有了属性定义,配置文件中也使用了这些属性,一切OK了吗?
还是不行,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>
为测试资源目录开启过滤
<resources>
<resource>
<directory>${project.basedir}/src/test/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
配置多个资源目录
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<resources>
<resource>
<directory>src/main/sql</directory>
<filtering>false</filtering>
</resource>
</resources>
其中src/main/resources开启了过滤,而src/main/sql没有启用过滤
执行mvn clean install -Pdev命令
mvn的-P参数表示在命令行激活一个profile,这里激活了id为dev的profile。构建完成后,输出目录中的数据库配置就是开发环境的配置了
为了构建差异的jdbc配置,我们做了哪些配置?
1、数据库配置的变化部分提取成了Maven属性
2、在POM的profile中定义了这些属性的值
3、为资源目录开启了属性过滤
4、最后需要在命令行激活profile,Maven就能够在构建项目的时候使用profile中属性值替换数据库配置文件中的属性引用
激活profile,Maven支持很多种激活Profile的方式
1、命令行激活
使用mvn命令行参数-P加上profile的id来激活profile,多个id之间以逗号分隔
使用命令激活dev-x和dev-y两个profile
mvn clean install -Pdev-x,dev-y
2、settings文件显式激活
如果用户希望某个profile默认一直处于激活状态,就可以配置settings.xml文件的activeProfiles元素,表示其配置的profile对于所有项目都处于激活状态
settings文件显式激活profile
<settings>
...
<activeProfiles>
<activeProfile>dev-x</activeProfile>
</activeProfiles>
...
</settings>
3、系统属性激活
配置当某系统属性存在的时候,自动激活profile
当系统属性test存在时,激活此profile
<profiles>
<profile>
<activation>
<property>
<name>test</name>
</propety>
</activation>
...
</profile>
</profiles>
当系统属性test存在,且值等于x的时候激活profile
<profiles>
<profile>
<activation>
<property>
<name>test</name>
<value>x</value>
</propety>
</activation>
...
</profile>
</profiles>
上面的配置,不要忘了在命令行声明系统属性
mvn clean install -Dtest=x
因此,这其实也是一种从命令行激活profile的方法,而且多个profile完全可以使用同一个系统属性来激活
4、操作系统环境激活
Profile还可以自动根据操作系统环境激活,如果构建在不同的操作系统有差异,用户完全可以将这些差异写进profile,然后配置它们自动基于操作系统激活
基于操作系统环境激活profile
<profiles>
<profile>
<activation>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
</activation>
...
</profile>
</profiles>
这里family的值包括Windows、UNIX和Mac等,而其他几项name、arch、version,用户可以通过查看环境中的系统属性os.name、os.arch、os.version获得
5、文件存在与否激活
Maven能够根据项目中某个文件存在与否来决定是否激活profile
基于文件存在与否激活profile
<profiles>
<profile>
<activation>
<file>
<missing>x.properties</missing>
<exists>y.properties</ exists >
</file>
</activation>
...
</profile>
</profiles>
6、默认激活
用户可以在定义profile的时候指定其默认激活
默认激活profile
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
...
</profile>
</profiles>
使用activeByDefault元素用户可以指定profile自动激活,不过需要注意的是,如果POM中有任何一个profile通过以上其他任意一种方式被激活了,所有的默认激活配置都会失效
项目中有很多profile,用户怎么知道哪个激活了?
maven-help-plugin提供一个目标帮助用户了解当前激活的profile
mvn help:active-profiles
maven-help-plugin还有另外一个目标用来列出当前所有的profile
mvn help:all-profiles
那么,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项目有效
4、profiles.xml(Maven 2):还可以在项目根目录下使用一个额外的profiles.xml文件来声明profile,不过该特性已经在Maven 3中被移除。建议用户将这类profile移到settings.xml中
POM中的profile可使用的元素
因为profile可以伴随pom.xml一起存在,所以可以使用很多POM元素
<project>
<repositories></repositories>
<pluginRepositories></pluginRepositories>
<distributationManagement></distributationManagement>
<dependencyManagement></dependencyManagement>
<modules></modules>
<properties></properties>
<reporting></reporting>
<build>
<plugins></plugins>
<defaultGoal></defaultGoal>
<resources></resources>
<testResources></testResources>
<finalName></finalName>
</build>
</project>
POM外部的profile可使用的元素
由于无法保证外部的profile随着特定的pom.xml一起分发,所以外部的profile可使用的元素很少
<project>
<repositories></respositories>
<pluginRepositories></pluginRepositories>
<properties></properties>
</project>
外部的profile仅仅能影响项目的仓库和Maven属性
Web资源过滤
在Web项目中,资源文件同样位于src/main/resources目录下,它们经处理后会位于WAR包的WEB-INF/classes目录下,这也是Java代码编译打包后的目录。也就是说,这类资源文件在打包过后位于应用程序的classpath中。
Web项目中海油另外一类资源文件,默认它们的源码位置src/main/webapp/目录,经打包后位于WAR包的根目录。
例如,一个Web项目的css源码文件在src/main/webapp/css/目录,项目打包后可以在WAR包的css/目录下找到对应的css文件。这一类资源文件称作web资源文件,它们在打包过后不位于应用程序的classpath中
与一般的资源文件一样,web资源文件默认不会被过滤。开启一般资源文件的过滤也不会影响到web资源文件
不过有的时候,我们可能希望在构建项目的时候,为不同的客户使用不一样的资源文件(例如客户的logo图片不同,或者css主题不同)。这时可以在web资源文件中使用Maven属性,例如用 ${client.logo}表示客户的logo图片,用${client.theme}表示客户的css主题。然后使用profile分别定义这些Maven属性的值
针对不同客户web资源的profile
<profiles>
<profile>
<id>client-a</id>
<properties>
<client.logo>a.jpg</client.logo>
<client.theme>red</client.theme>
</properties>
</profile>
<profile>
<id>client-b</id>
<properties>
<client.logo>b.jpg</client.logo>
<client.theme>blue</client.theme>
</properties>
</profile>
</profiles>
为web资源目录src/main/webapp/开启过滤
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<configuration>
<resource>
<filtering>true</filtering>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.css</include>
<include>**/*.js</include>
</includes>
</resource>
</configuration>
</plugin>
对上述配置进行解释
1、web资源目录src/main/webapp(这也是默认的web资源目录)
2、配置filtering开启过滤,并且使用includes指定要过滤的文件,这里是所有的css和js文件
3、激活某个profile进行构建,mvn clean install -Pclient-a,告诉web资源文件使用logo图片a.jpg,使用css主题red
在profile中激活集成测试?
什么是集成测试?
如何在Maven中使用TestNG?