乐趣区

Maven基础教程之多环境构建

这又是一个系列,一个要把 Maven 讲透的系列,希望能够对大家有帮助!

前言

这篇文章总结的多环境构建绝对是会在实际工作中会用到的内容。比如我现在的这家公司的 Maven 项目,基本上都使用了这篇文章将要总结的多环境构建。那到底什么是多环境构建呢?

我们想象一下这样的一个场景。一般我们的项目都会有开发环境、测试环境和生产环境,这些环境的数据库等配置基本上都是不一样的,那么我们在进行项目构建的时候就需要能够识别所在的环境并使用正确的配置数据。对于多个环境,我们如何能够灵活的使用不同的配置数据呢?

在 Maven 中,为了灵活的支持这种场景,内置了三大特性,即属性、Profile 和资源过滤。下面我们将通过具体的代码示例来细说这三大特性。

Maven 属性

对于 Maven 属性,在前面的文章我们也接触过,比如之前是这样用的:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.jellythink.BookStore</groupId>
    <artifactId>project-A</artifactId>
    <version>1.0.0</version>

    <properties>
        <springframework.version>5.1.6.RELEASE</springframework.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springframework.version}</version>
        </dependency>
    </dependencies>
</project>

通过 <properties> 元素,我们可以自定义一个或多个 Maven 属性,然后在 POM 的其它地方使用 ${属性名称} 的方式引用该属性,这种做法的最大意义在于消除重复,便于后期统一修改。但是这不是 Maven 属性的全部,实际上,在 Maven 中包含以下六类属性:

  • 内置属性
    主要有以下两个常用内置属性:

    • ${basedir}表示项目根目录,即包含 pom.xml 文件的目录
    • ${version}表示项目版本
  • POM 属性
    我们可以使用 POM 属性引用 POM 文件中对应元素的值,比如 ${project.artifactId} 就对应了 <project><artifactId> 元素的值,常用的 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}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
这些属性都对应一个 POM 元素,有一些属性的默认值都是在超级 POM 中定义的。
  • 自定义属性
    自定义属性就是通过 <properties> 元素定义的属性,最开始的例子就已经讲的很明白了。
  • Settings 属性
    大家还记得 Maven 中的 settings.xml 文件吗?不记得的伙伴可以去看下这篇《Maven 基础教程之安装与配置》。而这个 Settings 属性就表示我们可以使用以 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 查看所有的 Java 系统属性。

正确的使用这些 Maven 属性可以帮助我们简化 POM 的配置和维护工作。

资源过滤

在我们开发过程中,经常会碰到这样的配置文件:

database.jdbc.driverClass = com.mysql.jdbc.driverClass
database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
database.jdbc.username = develop
database.jdbc.password = develop-password

上面的配置数据只是对开发人员的,如果测试人员进行时,则使用的如下这样的一套配置文件:

database.jdbc.driverClass = com.mysql.jdbc.driverClass
database.jdbc.connectionURL = jdbc:mysql://localhost:3306/test
database.jdbc.username = test
database.jdbc.password = test-password

也就是说,在开发环境和测试环境,我们需要使用不同的配置文件,在没有使用 Maven 之前,我们都是手动的修改对应的配置数据,话又说回来了,这样很麻烦,还很容易出错。现在有了 Maven,我们需要作出一点改变。

为了应对不同的使用环境,我们需要将配置文件中变化的部分使用 Maven 属性替换,比如上面的配置文件,我们需要修改成这个样子:

database.jdbc.driverClass = ${db.driver}
database.jdbc.connectionURL = ${db.url}
database.jdbc.username = ${db.username}
database.jdbc.password = ${db.password}

我们在配置文件中定义了四个 Maven 属性:db.driver、db.url、db.username 和 db.password。接下来,我们就需要在某个地方定义这些属性。在 Maven 中,我们只需要使用一个额外的 profile 来定义这些属性就可以了。

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <db.driver>com.mysql.jdbc.driverClass</db.driver>
            <db.url>jdbc:mysql://localhost:3306/dev</db.url>
            <db.username>develop</db.username>
            <db.password>develop-password</db.password>
        </properties>
    </profile>
</profiles>

这里通过 profile 定义了这些属性,并使用了一个 id 为 dev 的值来区别这个 profile,这样以后我们就可以针对不同的环境定义不同的 profile,就可以非常的灵活。

有了属性定义,配置文件中也使用了这些属性,这样就可以了吗?不是这么简单的!我们都知道,Maven 属性默认只有在 POM 中才会被解析。也就是说,${db.username}放到 POM 中会被解析成 develop,但是如果放到 src/main/resources/ 目录下的文件中,构建的时候它还是 ${db.username}。所以,我们需要让 Maven 解析资源文件中的 Maven 属性。

资源文件的处理其实是 maven-resources-plugin 的工作,但是它默认的行为只是将项目主资源文件复制到主代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中。我们只需要开启资源过滤,这个插件就能够解析资源文件中的 Maven 属性。

为主资源目录开启过滤:

<build>
    <!-- 为主资源目录开启过滤 -->
    <resources>
        <resource>
            <directory>${project.basedir}/src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
    
    <!-- 为测试资源目录开启过滤 -->
    <testResources>
        <testResource>
            <directory>${project.basedir}/src/main/resources</directory>
            <filtering>true</filtering>
        </testResource>
    </testResources>
</build>

我们通过 mvn clean package -Pdev 命令进行构建。其中 -P 参数表示在命令行激活一个 profile。对于 profile 没有看懂,不要紧,下面我们再细说。构建完成后,输出目录中的数据库配置就是开发环境的配置了:

database.jdbc.driverClass = com.mysql.jdbc.driverClass
database.jdbc.connectionURL = jdbc:mysql://localhost:3306/dev
database.jdbc.username = develop
database.jdbc.password = develop-password

Maven Profile

上面说到 profile,对于 profile 大家可能还非常的懵,这里就对 profile 进行详细的总结。profile 是一个非常有用的功能,至少在我们公司的项目中大量使用。profile 是专为不同的环境,实现无缝迁移而定制的。我们可以不同的环境配置不同的 profile,比如上面提到的开发环境和测试环境两种环境下不同的配置信息,我们可以通过 profile 进行配置:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <db.driver>com.mysql.jdbc.driverClass</db.driver>
            <db.url>jdbc:mysql://localhost:3306/dev</db.url>
            <db.username>develop</db.username>
            <db.password>develop-password</db.password>
        </properties>
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <db.driver>com.mysql.jdbc.driverClass</db.driver>
            <db.url>jdbc:mysql://localhost:3306/test</db.url>
            <db.username>test</db.username>
            <db.password>test-password</db.password>
        </properties>
    </profile>
</profiles>

可以看到,同样的属性在两个 profile 中的值是不一样的;同样的,我们还可以添加更多的 profile 配置,比如生产配置等。接下来,我们在构建应用时,可以使用 -Pdev 激活 dev profile,使用 -Ptest 激活 test profile。

总结

关于 Maven 的多环境构建,这篇文章基本上把我工作中遇到的各种语法都进行了总结,而且这些用法也比较有代表性,希望通过我这里的总结,对大家有所帮助,在日后的工作中,大家遇到这种语法不会搞到陌生;也更希望大家在构建自己的项目中,可以使用这些用法来提升自己的工作效率。

果冻想,玩代码,玩技术!

2019 年 5 月 5 日,于内蒙古呼和浩特。


退出移动版