Maven基本使用(下)
Maven基本使用(下)
11、灵活的构建
Maven 为了支持构建的灵活性,内置了三大特性,即属性、Profile 和资源过滤。本文介绍如何合理使用这些特性
来帮助项目自如地应对各种环境。
11.1 Maven属性
前面已经简单介绍过 Maven 属性的使用,如下所示:
<properties>
<junit.version>4.13.2</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
这可能是最常见的使用 Maven 属性的方式,通过 <properties>
元素用户可以自定义一个或多个 Maven 属性,
然后在 POM 的其他地方使用 ${属性名称}
的方式引用该属性,这种做法的最大意义在于消除重复。这样本来需要
在多个地方重复声明同样的 junit 版本,现在只在一个地方声明就可以,重复越多,好处就越明显。因为这样不仅
减少了日后升级版本的工作量,也能降低错误发生的概率。
这不是 Maven 属性的全部,事实上这只是 6 类 Maven 属性中的一类而已。这 6 类属性分别为:
-
内置属性:主要有两个常用内置属性
${basedir}
表示项目根目录,即包含 pom.xml 文件的目录;${version}
表示项目版本。 -
POM 属性:用户可以使用该类属性引用 POM 文件中对应元素的值。例如
${project.artifactld}
就对应了
<project><artifactld>
元素的值,常用的 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.groupld}
:项目的 groupld。${project.artifactId}
:项目的 artifactld。${project.version}
:项目的 version,与${version}
等价。${project.build.finalName}
:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
。这些属性都对应了一个 POM 元素,它们中一些属性的默认值都是在超级 POM 中定义的。
-
自定义属性:用户可以在 POM 的
<properties>
元素下自定义 Maven 属性。例如:<project> ... <properties> <my.prop>hello</my.prop> </properties> ... </project>
然后在 POM 中其他地方使用
${my.prop}
的时候会被替换成 hello。 -
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
查看所有的环境变量。
正确使用这些 Maven 属性可以帮助我们简化 POM 的配置和维护工作,下面列举几个常见的Maven 属性使用样
例。
<properties>
<!-- 项目的主源码目录,默认为src/main/java/ -->
<a>${pom.build.sourceDirectory}</a>
<!-- 项目的测试源码目录,默认为src/test/java/ -->
<b>${project.build.testSourceDirectory}</b>
<!-- 项目构建输出目录,默认为target/ -->
<c>${project.build.directory}</c>
<!-- 项目主代码编译输出目录,默认为target/classes -->
<d>${project.build.outputDirectory}</d>
<!-- 项目测试代码编译输出目录,默认为target/test-classes -->
<e>${project.build.testOutputDirectory}</e>
<!-- 项目的groupId -->
<f>${project.groupId}</f>
<!-- 项目的artifactId -->
<g>${project.artifactId}</g>
<!-- 项目的version,与${version}等价-->
<h>${project.version}</h>
<!-- 项目打包输出文件的名称,默认为${project.artifactId}-${project.version} -->
<i>${project.build.finalName}</i>
<!-- setting属性 -->
<!-- 获取本地仓库地址-->
<a1>${settings.localRepository}</a1>
<!-- 系统属性 -->
<!-- 用户目录 -->
<a2>${user.home}</a2>
<!-- 环境变量属性,获取环境变量JAVA_HOME的值 -->
<a3>${env.JAVA_HOME}</a3>
</properties>
在一个多模块项目中,模块之间的依赖比较常见,这些模块通常会使用同样的 groupId 和 version,因此这个时候
就可以使用POM 属性。
<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>
大量的 Maven 插件用到了 Maven 属性,这意味着在配置插件的时候同样可以使用 Maven 属性来方便地自定义插
件行为。前面我们知道,maven-surefire-plugin 运行后默认的测试报告目录为 target/surefire-reports,这实际
上就是 ${project.build.directory}/surefire-reports
,如果查阅该插件的文档,会发现该插件提供了
reportsDirectory 参数来配置测试报告目录。因此如果想要改变测试报告目录,例如改成 target/test-reports,就
可以这样配置。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<reportsDirectory>${project.build.directory}/test-reports</reportsDirectory>
</configuration>
</plugin>
从上面的内容中可以看到,Maven 属性能让我们在 POM 中方便地引用项目环境和构建环境的各种十分有用的
值,这是创建灵活构建的基础。下面将会结合 profile 和资源过滤,展示 Maven 能够为构建提供的更多的可能
性。
11.2 构建环境的差异
在不同的环境中,项目的源码应该使用不同的方式进行构建,最常见的就是数据库配置了。例如在开发的过程中,
有些项目会在 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://192.168.1.100:3306/test
database.jdbc.username = test
database.jdbc.password = test-pwd
连接数据库的URL、用户名和密码都发生了变化,类似地,当项目被发布到产品环境的时候,所使用的数据库配置
又是另外一套了。这个时候,比较原始的做法是,使用与开发环境一样的构建,然后在测试或者发布产品之前再手
动更改这些配置。这是可行的,也是比较常见的,但肯定不是最好的方法。这里已经不止一次强调,手动往往就意
味着低效和错误,因此需要找到一种方法,使它能够自动地应对构建环境的差异。
Maven 的答案是针对不同的环境生成不同的构件。也就是说,在构建项目的过程中,Maven 就已经将这种差异处
理好了。
11.3 资源过滤
为了应对环境的变化,首先需要使用 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 属性,就应该在某个地方定义它们。前面介绍过如何自定义 Maven 属性,这里要做的是使用
一个额外的 profile 将其包裹。
<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-test</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 配置即可。
<build>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
类似地,下面的配置为测试资源目录开启了过滤。
<resource>
<directory>${project.basedir}/src/test/resources</directory>
<filtering>true</filtering>
</resource>
读者可能还会从上述代码中意识到,主资源目录和测试资源目录都可以超过一个,虽然会破坏 Maven 的约定,但
Maven 允许用户声明多个资源目录,并且为每个资源目录提供不同的过滤配置。
<resource>
<directory>src/main/sql</directory>
<filtering>false</filtering>
</resource>
上述代码配置了两个资源目录,其中 src/main/resources 开启了过滤,而 src/main/sql 没有启用过滤。
到目前为止一切基本就绪了,我们将数据库配置的变化部分提取成了 Maven 属性,在 POM 的 profile 中定义了这
些属性的值,并且为资源目录开启了属性过滤。最后,只需要在命令行激活 profile,Maven 就能够在构建项目的
时候使用 profile 中属性值替换数据库配置文件中的属性引用。运行命令如下:
$ mvn clean install -Pdev
mvn 的 -P 参数表示在命令行激活一个 profile。这里激活了 id 为 dev 的 profile。构建完成后,输出目录中的数据
库配置就是开发环境的配置了:
database.jdbc.driverClass=com.mysql.jdbc.Driver
database.jdbc.connectionURL=jdbc:mysql://localhost:3306/test
database.jdbc.username=dev
database.jdbc.password=dev-pwd
11.3.1 设置资源文件内容动态替换
资源文件中可以通过${maven属性}来引用maven属性中的值,打包的过程中这些会被替换掉,替换的过程默认是
不开启的,需要手动开启配置。
修改 src/main/resource/jdbc.properties
内容如下:
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}
修改 src/test/resource/jdbc.properties
内容如下:
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}
<properties>
<!-- 指定资源文件复制过程中采用的编码方式 -->
<encoding>UTF-8</encoding>
<jdbc.url>jdbc:mysql://localhost:3306/javacode2018?characterEncoding=UTF-8</jdbc.url>
<jdbc.username>root</jdbc.username>
<jdbc.password>root</jdbc.password>
</properties>
<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>
注意上面开启动态替换的元素是filtering。
上面build元素中的resources和testResources是用来控制构建过程中资源文件配置信息的,比资源文件位于哪个
目录,需要复制到那个目录,是否开启动态过滤等信息。
resources元素中可以包含多个resource,每个resource表示一个资源的配置信息,一般使用来控制主资源的复制
的。
testResources元素和testResources类似,是用来控制测试资源复制的。
target中的2个资源文件内容被动态替换掉了。
11.3.2 自定义替换的分隔符
上面会将资源文件中 的 内 容 使 用 m a v e n 属 性 中 的 值 进 行 替 换 , {}的内容使用maven属性中的值进行替换, 的内容使用maven属性中的值进行替换,{}中包含的内容默认会被替换,那么我们是否可
以自定义${}这个格式,比如我希望被##包含内容进行替换,这个就涉及到替换中分隔符的指定了,需要设置插件
的一些参数。
自定义分隔符,需要我们配置maven-resources-plugin插件的参数,如下:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<!-- 是否使用默认的分隔符,默认分隔符是${*}和@ ,这个地方设置为false,表示不启用默认分隔符配置-->
<useDefaultDelimiters>false</useDefaultDelimiters>
<!-- 自定义分隔符 -->
<delimiters>
<delimiter>$*$</delimiter>
<delimiter>#*#</delimiter>
</delimiters>
</configuration>
</plugin>
</plugins>
delimiters中可以配置多个delimiter,可以配置 #*#
,其中的*表示属性名称,那么资源文件中的 #属性名# 在复
制的过程中会被替换掉。
修改配置文件内容:
jdbc.url=${jdbc.url}
jdbc.username=$jdbc.username$
jdbc.password=##jdbc.password##
不想让cont.properties被复制到target/classes目录:
<resources>
<resource>
<!-- 指定资源文件的目录 -->
<directory>${project.basedir}/src/main/resources</directory>
<!-- 是否开启过滤替换配置,默认是不开启的 -->
<filtering>true</filtering>
<includes>
<include>**/jdbc.properties</include>
</includes>
<excludes>
<exclude>**/const.properties</exclude>
</excludes>
</resource>
</resources>
上面使用includes列出需要被处理的,使用excludes排除需要被处理的资源文件列表,采用通配符的写法,**
匹
配任意深度的文件路径,*
匹配任意个字符。
如果此时我想让const.propertis只是被复制到target下面,但是不要去替换里面的内容:
<resources>
<resource>
<!-- 指定资源文件的目录 -->
<directory>${project.basedir}/src/main/resources</directory>
<!-- 是否开启过滤替换配置,默认是不开启的 -->
<filtering>true</filtering>
<includes>
<include>**/jdbc.properties</include>
</includes>
</resource>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>**/const.properties</include>
</includes>
</resource>
</resources>
11.4 Maven Profile
从前面内容我们看到,不同环境的构建很可能是不同的,典型的情况就是数据库的配置。除此之外,有些环境可能
需要配置插件使用本地文件,或者使用特殊版本的依赖,或者需要一个特殊的构件名称。要想使得一个构建不做任
何修改就能在任何环境下运行,往往是不可能的。为了能让构建在各个环境下方便地移植,Maven 引入了 profile
的概念。profile 能够在构建的时候修改 POM 的一个子集,或者添加额外的配置元素。用户可以使用很多方式激
活 profile,以实现构建在不同环境下的移植。
maven支持让我们配置多套环境,每套环境中可以指定自己的maven属性,mvn命令对模块进行构建的时候可以
通过-P参数来指定具体使用哪个环境的配置。profiles元素支持定义多套环境的配置信息,配置如下用法:
<profiles>
<profile>测试环境配置信息</profile>
<profile>开发环境配置信息</profile>
<profile>线上环境配置信息</profile>
<profile>环境n配置信息</profile>
</profiles>
profiles中包含多个profile元素,每个profile可以表示一套环境,profile示例如下:
<profile>
<id>dev</id>
<properties>
<jdbc.url>dev jdbc url</jdbc.url>
<jdbc.username>dev jdbc username</jdbc.username>
<jdbc.password>dev jdbc password</jdbc.password>
</properties>
</profile>
id表示这套环境的标识信息,properties 可以定义环境中使用到的属性列表。
执行 mvn 命令编译的时候可以带上一个 -P profileid
来使用指定的环境进行构建。
11.4.1 针对不同环境的profile
继续前面小节介绍的数据库差异为例,下面引入了一个针对开发环境的 profile,类似地,可以加入测试环境和产
品环境的 profile。
<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>
<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>
</profile>
</profiles>
$ mvn help:all-profiles
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maven hello world 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.3.0:all-profiles (default-cli) @ hello-maven ---
[INFO] Listing Profiles for Project: org.example:hello-maven:jar:1.0-SNAPSHOT
Profile Id: jdk-1.8 (Active: true , Source: settings.xml)
Profile Id: dev (Active: false , Source: pom)
Profile Id: test (Active: false , Source: pom)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.573 s
[INFO] Finished at: 2023-10-19T21:09:08+08:00
[INFO] Final Memory: 12M/245M
[INFO] ------------------------------------------------------------------------
$ mvn help:active-profiles
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maven hello world 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.3.0:active-profiles (default-cli) @ hello-maven ---
[INFO]
Active Profiles for Project 'org.example:hello-maven:jar:1.0-SNAPSHOT':
The following profiles are active:
- jdk-1.8 (source: external)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.595 s
[INFO] Finished at: 2023-10-19T21:08:31+08:00
[INFO] Final Memory: 12M/245M
[INFO] ------------------------------------------------------------------------
11.4.2 激活 profile
为了尽可能方便用户,Maven 支持很多种激活 Profile 的方式。
11.4.3 命令行激活
用户可以使用 mvn 命令行参数 -P 加上 profile 的 id 来激活 profile,多个 id 之间以逗号分隔。例如,下面的命令
激活了 dev 和 test 两个 profile:
$ mvn clean install -Pdev,test
11.4.4 settings 文件显式激活
如果用户希望某个 profile 默认一直处于激活状态,就可以配置 settings.xml 文件的 activeProfiles 元素,表示其
配置的 profile 对于所有项目都处于激活状态。
<settings>
<activeProfiles>
<activeProfile>dev</activeProfile>
</activeProfiles>
</settings>
前面就曾经用到这种方式默认激活了一个关于仓库配置的 profile。
$ mvn help:active-profiles
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maven hello world 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.3.0:active-profiles (default-cli) @ hello-maven ---
[INFO]
Active Profiles for Project 'org.example:hello-maven:jar:1.0-SNAPSHOT':
The following profiles are active:
- jdk-1.8 (source: external)
- dev (source: org.example:hello-maven:1.0-SNAPSHOT)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.573 s
[INFO] Finished at: 2023-10-19T21:14:08+08:00
[INFO] Final Memory: 12M/245M
[INFO] ------------------------------------------------------------------------
11.4.5 系统属性激活
用户可以配置当某系统属性存在的时候,自动激活 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>
<activation>
<property>
<name>env</name>
</property>
</activation>
</profile>
$ set env=test
$ mvn clean install -Denv
可以进一步配置当某系统属性 env 存在,且值等于 test 的时候激活 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>
<activation>
<property>
<name>env</name>
<value>test</value>
</property>
</activation>
</profile>
不要忘了,用户可以在命令行声明系统属性。例如:
$ set env=test
$ mvn clean install -Denv=test
因此,这其实也是一种从命令行激活 profile 的方法,而且多个 profile 完全可以使用同一个系统属性来激活。
11.4.6 操作系统环境激活
Profile 还可以自动根据操作系统环境激活,如果构建在不同的操作系统有差异,用户完全可以将这些差异写进
profile,然后配置它们自动基于操作系统环境激活。
<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>
<activation>
<os>
<name>Windows XP</name>
<family>Windows</family>
<arch>x86</arch>
<version>5.1.2600</version>
</os>
</activation>
</profile>
这里 family 的值包括 Windows、UNIX 和 Mac等,而其他几项 name、arch、version,用户可以通过查看环境
中的系统属性 os.name
、os.arch
、os.version
获得。
11.4.7 文件存在与否激活
Maven 能够根据项目中某个文件存在与否来决定是否激活 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>
<activation>
<file>
<missing>${project.basedir}/src/main/resources/x.properties</missing>
<exists>${project.basedir}/src/main/resources/y.properties</exists>
</file>
</activation>
</profile>
11.4.8 默认激活
用户可以在定义 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>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
使用 activeByDefault 元素用户可以指定 profile 自动激活。不过需要注意的是,如果 POM 中有任何一个 profile
通过以上其他任意一种方式被激活了,所有的默认激活配置都会失效。
activeByDefault 表示默认开启这个环境的配置,默认值是false,这个地方我们设置为true,表示开启默认配置。
$ mvn help:active-profiles
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building maven hello world 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-help-plugin:3.3.0:active-profiles (default-cli) @ hello-maven ---
[INFO]
Active Profiles for Project 'org.example:hello-maven:jar:1.0-SNAPSHOT':
The following profiles are active:
- jdk-1.8 (source: external)
- test (source: org.example:hello-maven:1.0-SNAPSHOT)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.578 s
[INFO] Finished at: 2023-10-19T21:43:12+08:00
[INFO] Final Memory: 13M/309M
[INFO] ------------------------------------------------------------------------
如果项目中有很多的 profile,它们的激活方式各异,用户怎么知道哪些 profile 被激活了呢?maven-help-plugin
提供了一个目标帮助用户了解当前激活的profile:
$ mvn help:active-profiles
maven-help-plugin 还有另外一个目标用来列出当前所有的 profile:
$ mvn help:all-profiles
11.4.9 将数据库所有的配置放在一个文件中
maven 支持我们这么做,可以在 profile 中指定一个外部属性文件 xx.properties,文件内容是这种格式的:
key1=value1
key2=value2
......
keyn=valuen
然后在profile元素中加入下面配置:
<build>
<filters>
<filter>xx.properties文件路径(相对路径或者完整路径)</filter>
</filters>
</build>
上面的filter元素可以指定多个,当有多个外部属性配置文件的时候,可以指定多个来进行引用。
然后资源文件复制的时候就可以使用下面的方式引用外部资源文件的内容:xxx=${key1}
新建文件 b2b/config/dev.properties
,内容如下:
# b2b-account-service jdbc配置信息
b2b-account-service.jdbc.url=dev_account_jdbc_url
b2b-account-service.jdbc.username=dev_account_jdbc_username
b2b-account-service.jdbc.password=dev_account_jdbc_password
# b2b-order-service jdbc配置信息
b2b-order-service.jdbc.url=dev_order_jdbc_url
b2b-order-service.jdbc.username=dev_order_jdbc_username
b2b-order-service.jdbc.password=dev_order_jdbc_password
新建文件 b2b/config/test.properties
,内容如下:
# b2b-account-service jdbc配置信息
b2b-account-service.jdbc.url=test_account_jdbc_url
b2b-account-service.jdbc.username=test_account_jdbc_username
b2b-account-service.jdbc.password=test_account_jdbc_password
# b2b-order-service jdbc配置信息
b2b-order-service.jdbc.url=test_order_jdbc_url
b2b-order-service.jdbc.username=test_order_jdbc_username
b2b-order-service.jdbc.password=test_order_jdbc_password
新建文件 b2b/config/prod.properties
,内容如下:
# b2b-account-service jdbc配置信息
b2b-account-service.jdbc.url=prod_account_jdbc_url
b2b-account-service.jdbc.username=prod_account_jdbc_username
b2b-account-service.jdbc.password=prod_account_jdbc_password
# b2b-order-service jdbc配置信息
b2b-order-service.jdbc.url=prod_order_jdbc_url
b2b-order-service.jdbc.username=prod_order_jdbc_username
b2b-order-service.jdbc.password=prod_order_jdbc_password
b2b-account-service/src/main/resource/jdbc.properties
文件内容:
jdbc.url=${b2b-account-service.jdbc.url}
jdbc.username=${b2b-account-service.jdbc.username}
jdbc.password=${b2b-account-service.jdbc.password}
${}
中的属性名称取的是上面 b2b/config
目录中资源文件中的属性名称。
b2b-order-service/src/main/resource/jdbc.properties
文件内容:
jdbc.url=${b2b-order-service.jdbc.url}
jdbc.username=${b2b-order-service.jdbc.username}
jdbc.password=${b2b-order-service.jdbc.password}
pom
文件的配置:
<!-- 配置多套环境 -->
<profiles>
<!-- 开发环境使用的配置 -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>env</name>
<value>env_dev</value>
</property>
</activation>
<build>
<filters>
<filter>../../config/dev.properties</filter>
</filters>
</build>
</profile>
<!-- 测试环境使用的配置 -->
<profile>
<id>test</id>
<activation>
<property>
<name>env</name>
<value>env_test</value>
</property>
</activation>
<build>
<filters>
<filter>../../config/test.properties</filter>
</filters>
</build>
</profile>
<!-- 线上环境使用的配置 -->
<profile>
<id>prod</id>
<activation>
<property>
<name>env</name>
<value>env_prod</value>
</property>
</activation>
<build>
<filters>
<filter>../../config/prod.properties</filter>
</filters>
</build>
</profile>
</profiles>
<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>
# 执行
$ mvn clean package -Pdev
11.5 profile的种类
根据具体的需要,可以在以下位置声明 profile:
-
pom.xml
:很显然,pom.xml
中声明的 profile 只对当前项目有效。 -
用户
settings.xml
:用户目录下.m2/settings.xml
中的 profile 对本机上该用户所有的 Maven 项目有效。
-
全局
settings.xml
: Maven 安装目录下conf/settings.xml
中的 profile 对本机上所有的 Maven 项目有效。
-
profiles.xml
(Maven 2):还可以在项目根目录下使用一个额外的profiles.xml
文件来声明 profile,不过该特性已经在 Maven 3 中被移除。建议用户将这类 profile 移到 settings.xml 中。
前面已经解释过,为了不影响其他用户且方便升级 Maven,用户应该选择配置用户范围的 settings.xml,避免修
改全局范围的 settings.xml 文件。也正是因为这个原因,一般不会在全局的 settings.xml 文件中添加 profile。
像 profiles.xml 这样的文件,默认是不会被 Maven 安装到本地仓库,或者部署到远程仓库的。因此一般来说应该
避免使用,Maven 3也不再支持该特性。但如果在用 Maven 2,而且需要为几十或者上百个客户执行不同的构
建,往 POM 中放置这么多的 profile 可能就不太好。这时可以选择使用 profiles.xml。
如果是 Maven3,则应该把这些内容移动到 settings.xml中。
不同类型的 profile 中可以声明的 POM 元素也是不同的,pom.xml 中的 profile 能够随着 pom.xml 一起被提交到
代码仓库中、被Maven 安装到本地仓库中、被部署到远程 Maven 仓库中。换言之,可以保证该 profile 伴随着某
个特定的 pom.xml 一起存在,因此它可以修改或者增加很多 POM 元素。
POM 中的 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>
从上面可以看到,可供 pom 中 profile 使用的元素非常多,在 pom profile 中用户可以修改或添加仓库、插件仓
库以及部署仓库地址;可以修改或者添加项目依赖;可以修改聚合项目的聚合配置;可以自由添加或修改 Maven
属性;添加或修改项目报告配置;pom profile 还可以添加或修改插件配置、项目资源目录和测试资源目录配置以
及项目构件的默认名称。
与 pom.xml 中的 profile 对应的,是其他三种外部的 profile,由于无法保证它们能够随着特定的 pom.xml一起被
分发,因此 Maven 不允许它们添加或者修改绝大部分的 pom 元素。
举个简单的例子。假设用户 Jack 在自己的 settings. xml 文件中配置了一个 profile,为了让项目A构建成功,Jack
在这个 profile 中声明几个依赖和几个插件,然后通过激活该 profile 将项目构建成功了。但是,当其他人获得项
目A的源码后,它们并没有Jack settings.xml 中的 profile,因此它们无法构建项目,这就导致了构建的移植性问
题。为了避免这种问题的出现,Maven 不允许用户在 settings.xml 的profile 中声明依赖或者插件。事实上,在
pom.xml 外部的 profile 只能够声明如代码清单所示几个元素。
<project>
<repositories></repositories>
<pluginRepositories></pluginRepositories>
<properties></properties>
</project>
现在不用担心 POM 外部的 profile 会对项目产生太大的影响了,事实上这样的 profile 仅仅能用来影响到项目的仓
库和 Maven 属性。
11.6 Web资源过滤
我们可能希望在构建项目的时候,为不同的客户使用不一样的资源文件(例如客户的 logo 图片不同,或者 css 主题
不同)。这时可以在 web 资源文件中使用 Maven 属性,例如用 ${client.logo}
表示客户的 logo 图片,用
${client.theme}
表示客户的 css 主题。然后使用 profile 分别定义这些 Maven 属性的值。
<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.log>b.jpg</client.log>
<client.theme>blue</client.theme>
</properties>
</profile>
</profiles>
需要配置 maven-war-plugin 对 src/main/webapp/ 这一web 资源目录开启过滤:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.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>
代码中声明了 web 资源目录 src/main/webapp(这也是默认的 web 资源目录),然后配置 filtering 开启过滤,并
且使用 includes 指定要过滤的文件,这里是所有css 和 js 文件。读者可以模仿上述配置添加额外的 web 资源目
录,选择是否开启过滤,以及包含或者排除一些该目录下的文件。
配置完成后,可以选择激活某个 profile 进行构建,如 mvn clean install -Pclient-a
,告诉 web 资源文件使
用 logo图片 a.jpg,使用css主题red。
11.7 在profile中激活集成测试
很多项目都有大量的单元测试和集成测试,单元测试的粒度较细,运行较快,集成测试粒度较粗,运行比较耗时。
在构建项目或者做持续集成的时候,我们都应当尽量运行所有的测试用例,但是当集成测试比较多的时候,高频率
地运行它们就会变得不现实。因此有一种更为合理的做法。例如,每次构建时只运行所有的单元测试,因为这不会
消耗太多的时间(可能小于5分钟),然后以一个相对低一点的频率执行所有集成测试(例如每天2次)。
TestNG中组的概念能够很好地支持单元测试和集成测试的分类标记。例如,可以使用如下的标注表示一个测试方
法属于单元测试:
@Test(groups={"unit"})
然后使用类似的标注表示某个测试方法为集成测试:
@Test(groups={"integration"})
使用上述方法可以很方便清晰地声明每个测试方法所属的类别。下面的工作就是告诉 Maven 默认只执行所有的单
元测试,只在特定的时候才执行集成测试。
<profiles>
<profile>
<id>full</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<groups>unit,integration</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<groups>unit</groups>
</configuration>
</plugin>
</plugins>
</build>
首先配置了 maven-surefire-plugin 执行 unit 测试,也就是说默认 Maven 只会执行单元测试。如果想要执行集成
测试,就需要激活 full profile,在这个 profile 中配置了 maven-surefire-plugin 执行 unit 和 integration 两个测
试组。
有了上述配置,用户就可以根据实际情况配置持续集成服务器。例如,每隔15分钟检查源码更新,如有更新则进
行一次默认构建,即只包含单元测试。此外,还可以配置一个定时的任务。例如,每天执行两次,执行一个激活
full profile的构建,以包含所有的集成测试。
从该例中可以看到,profile 不仅可以用来应对不同的构建环境以保持构建的可移植性,还可以用来分离构建的一
些较耗时或者耗资源的行为,并给予更合适的构建频率。
12、配置repositories、distributionManagement、pluginRepositories详解&&将已有jar包部署到私服
12.1 repositorie结构说明
repositorie 表示下载项目依赖库文件的 maven 仓库地址:
<repositories>
<repository>
<!-- 仓库ID -->
<id>nexus</id>
<!-- 仓库名称 -->
<name>Nexus</name>
<!-- 仓库地址 -->
<url>http://192.168.1.x:xxxx/repository/maven-public/</url>
<!-- 仓库中版本为releases的构件 -->
<releases>
<!-- 是否支持更新-->
<enabled>true</enabled>
<!-- 构件更新的策略,可选值有daily, always, never, interval:X(其中的X是一个数字,表示间隔的时间,单位min),默认为daily-->
<updatePolicy>always</updatePolicy>
<!-- 校验码异常的策略,可选值有ignore, fail, warn -->
<checksumPolicy>warn</checksumPolicy>
</releases>
<!-- 仓库版本为snapshots的构件-->
<snapshots>
<!-- 是否支持更新-->
<enabled>true</enabled>
<!-- 同上 -->
<updatePolicy>always</updatePolicy>
<!-- 同上 -->
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</repository>
</repositories>
在 repositories 元素下,可以使用 repository 子元素声明一个或者多个远程仓库。
repository元素说明:
-
id
:远程仓库的一个标识,中央仓库的id是central,所以添加远程仓库的时候,id不要和中央仓库的id重复,会把中央仓库的覆盖掉。
-
url
:远程仓库地址。 -
releases
:主要用来配置是否需要从这个远程仓库下载稳定版本构件。 -
snapshots
:主要用来配置是否需要从这个远程仓库下载快照版本构件。
releases和snapshots中有个enabled属性,是个boolean值,默认为true,表示是否需要从这个远程仓库中下载
稳定版本或者快照版本的构件,一般使用第三方的仓库,都是下载稳定版本的构件。
快照版本的构件以-SNAPSHOT结尾,稳定版没有这个标识。
如果是公司本地仓库,而且需要认证才能访问,需要在本地 maven 的 conf\settings.xml 文件中添加如下配置内
容:
<servers>
<server>
<id>nexus</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
12.2 distributionManagement结构说明
distributionManagement 表示项目打包成库文件后要上传到仓库地址:
<distributionManagement>
<!-- 正式版本 -->
<repository>
<uniqueVersion>false</uniqueVersion>
<!-- nexus服务器中用户名(settings.xml中<server>的id)-->
<id>releases</id>
<!-- 自定义名称 -->
<name>Releases Repository</name>
<url>http://192.168.1.x:xxxx/repository/maven-releases/</url>
<layout>default</layout>
</repository>
<!-- 快照 -->
<snapshotRepository>
<uniqueVersion>true</uniqueVersion>
<id>snapshots</id>
<name>Snapshots Repository</name>
<url>http://192.168.1.x:xxxx/repository/maven-snapshots/</url>
<layout>legacy</layout>
</snapshotRepository>
</distributionManagement>
如果是公司本地仓库,而且需要认证才能访问,也需要在本地 maven 的 conf\settings.xml 文件中添加如下配置
内容(< repository >节点下的< id >对应setting.xml文件中的server的id):
<servers>
<server>
<id>releases</id>
<username>admin</username>
<password>admin</password>
</server>
<server>
<id>snapshots</id>
<username>admin</username>
<password>admin</password>
</server>
</servers>
通过版本来控制上传到哪个仓库下:
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
snapshot属于快照版本,同一个snapshot版本的构件可以重复部署到私服中,如果私服中已经存在了则会进行覆
盖掉。而release是稳定版本的构件,重复部署会报错。
12.3 pluginRepositories 结构说明
pluginRepositories 表示插件的下载仓库地址,字段和用法与 repositories 中的 repository 基本一致:
<pluginRepositories>
<pluginRepository>
<id>nexus</id>
<name>Nexus</name>
<url>http://192.168.1.x:xxxx/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
<checksumPolicy>warn</checksumPolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
阿里云的配置:
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/nexus/content/repositories/central/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
13、上传本地仓库
$ mvn install:install-file -DgroupId=com.xxx -DartifactId=xxx-sdk -Dversion=1.0.0 -Dpackaging=jar -Dfile=xxx-xxx-sdk-1.0.0.jar
参数说明:
-DgroupId
:上面的groupId-DartifactId
:上面的artifactId-Dversion
:上面的version-Dpackaging
:jar-Dfile
:jar包的位置
14、将已有jar包部署到私服
14.1 meven私服连接配置
需要在 ./conf/setting.xml
中配置需要远程上传库的地址,用户以及密码(如果需要认证):
<servers>
<server>
<id>maven-releases</id>
<username>zm</username>
<password>123456</password>
</server>
</servers>
14.2 使用deploy命令上传
$ mvn deploy:deploy-file -Dmaven.test.skip=true -Dfile=E:\Work\MyWorkspace\idea\office-
pdf-util\src\main\resources\libs\aspose-words-16.8.0-jdk16.jar -DgroupId=aspose-words -
DartifactId=aspose-words -Dversion=16.8.0 -Dpackaging=jar -DrepositoryId=maven-releases
-Durl=http://192.168.0.167:8081/repository/maven-releases
在命令行指定 setting.xml:
$ mvn deploy:deploy-file --settings C:\Users\Liu\.m2\settings-zhiyi.xml -
Dmaven.test.skip=true -Dfile=E:\Work\MyWorkspace\idea\office-pdf-
util\src\main\resources\libs\aspose-words-16.8.0-jdk16.jar -DgroupId=aspose-words -
DartifactId=aspose-words -Dversion=16.8.0 -Dpackaging=jar -DrepositoryId=maven-releases
-Durl=http://192.168.0.167:8081/repository/maven-releases
各参数代表含义为:
-Dmaven.test.skip=true
:跳过编译、测试-Dfile=D:\work\thirdjar\aspose-words-16.8.0.jar
:jar包文件地址,绝对路径-DgroupId=pri.roy.mvn.test
:gruopId–pom坐标,自定义-DartifactId=mvn-api
:artifactId–pom坐标,自定义-Dversion
:版本号,自定义-Dpackaging
:打包方式-DrepositoryId
:远程库ID-Durl
:远程库地址
14.3 该操作常用来解决的问题
项目依赖第三方 jar 包,但 maven 中央仓库没有,这时可以选择下载后上传私服。
15、注意
15.1 插件的默认groupId
在pom.xml中配置插件的时候,如果是官方的插件,可以省略groupId。
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerVersion>1.8</compilerVersion>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
maven在解析该插件的时候,会自动给这个插件补上默认的官方的groupId,所以可以正常运行,但是不建议大家
这么使用,容易让新手比较懵逼。
15.2 查看最终pom文件
合并之后的 pom 文件。
$ mvn help:effective-pom > pom.xml
15.3 单继承问题
dependencyManagement 使用的时候有个问题,只有使用继承的时候,dependencyManagement中声明的依
赖才可能被子pom.xml用到,如果我的项目本来就有父pom.xml了,但是我现在想使用另外一个项目
dependencyManagement中声明的依赖,此时我们怎么办?这就是单继承的问题,这种情况在spring-boot、
spring-cloud中会遇到,所以大家需要注意。
当我们想在项目中使用另外一个构件中dependencyManagement声明的依赖,而又不想继承这个项目的时候,可
以在我们的项目中使用加入下面配置:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.javacode2018</groupId>
<artifactId>javacode2018-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>构件2</dependency>
<dependency>构件3</dependency>
<dependency>构件n</dependency>
</dependencies>
</dependencyManagement>
注意上面有两个关键元素:type的值必须是pom,scope元素的值必须是import。
上面这个配置会将javacode2018-parent构件中dependencyManagement元素中声明的所有依赖导入到当前
pom.xml的dependencyManagement中。
15.4 编码问题
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
编译代码的时候,涉及到资源文件和测试资源文件的拷贝,拷贝文件的时候涉及到文件的编码,这个是设置文件的
编码为UTF-8格式的。
其它写法:
<!-- pom中 -->
<encoding>UTF-8</encoding>
# mvn
$ mvn compile -Dencoding=UTF-8
$ mvn compile -Dproject.build.sourceEncoding=UTF-8