轻松搞定Maven多环境配置:实战技巧大放送

轻松搞定Maven多环境配置:实战技巧大放送

  1. 开发框架与中间件
  2. 2017.09.12
  3. 6 min read

Maven 多环境配置打包方式

使用 profiles

<profiles>
    <profile>
        <id>local</id>
        <properties>
            <environment>local</environment>
            <env>LOCAL</env>
        </properties>
    </profile>
    <profile>
        <id>dev</id>
        <properties>
            <environment>dev</environment>
            <env>DEV</env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <environment>test</environment>
            <env>TEST</env>
        </properties>
    </profile>
    <profile>
        <id>production</id>
        <properties>
            <environment>production</environment>
            <!-- 为什么要用大写? -->
            <env>PRODUCTION</env>
        </properties>
    </profile>
</profiles>

修改 build 添加 filter

<build>
    <filters>
        <!-- src/main/filters 为 maven 默认位置 -->
        <filter>src/main/filters/${environment}.yyyyy.properties</filter>
    </filters>
    <!-- 过滤 resources 下的所有配置文件 -->
    <resources>
        <resource>
            <!-- 配置需要被替换的资源文件路径 -->
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <!-- 加入 banner 打印输出文件 -->
                <include>**/*.banner</include>
                <!-- 加入 dubbo 配置文件 -->
                <include>**/dubbo/*</include>
            </includes>
            <excludes>
                <!-- 排除 jrebel 配置文件 -->
                <exclude>rebel.xml</exclude>
            </excludes>
        </resource>
    </resources>
    <!-- junit 测试用, 不将 junit 使用的配置打包 -->
    <testResources>
        <testResource>
            <directory>src/test/resources</directory>
            <filtering>true</filtering>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
                <include>**/*.banner</include>
            </includes>
        </testResource>
    </testResources>
</build>

使用命令打包

mvn clean install -Dmaven.test.skip=true -Pdev

使用 @ 代替 $

如果 resource 中存在 js, 且与 maven 内置变量相同, 会将 $ 全部替换为 maven 的参数 所有这里使用 @ 替换掉 $.

原理

maven 会使用 filter 中的文件属性替换所有 resource include 的文件中的占位符

使用 context:property-placeholder

有些参数在某些阶段中是常量。

  1. 在开发阶段我们连接数据库时的 url,username,password 等信息
  2. 分布式应用中 client 端的 server 地址,端口等

这些参数在不同阶段之间又往往需要改变

期望:有一种方案可以方便我们在一个阶段内不需要频繁写一个参数的值,而在不同阶段间又可以方便的切换参数的配置信息 解决:spring3 中提供了一种简便的方式就是 <content:property-placeholder> 元素 只需要在 spring 配置文件中添加一句:

<context:property-placeholder location="classpath:jdbc.properties"/>

或者

<bean id="propertyPlaceholderConfigurer" class="org.springframework,beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>jdbc.properties<value/>
        </list>
    </property>
</bean>

即可,这里的 location 值为参数配置文件的位置,配置文件通常放到 src 目录下,参数配置文件的格式即键值对的形式,

#jdbc 配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root

行内 #号后面部分为注释

这样一来就可以为 spring 配置的 bean 的属性设置值了,比如 spring 有一个数据源的类

<bean id="dataSource" class="org.springframework,jdbc,datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${driverClassName}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</bean>

甚至可以将 ${} 这种形式的变量用在 spring 提供的注解当中,为注解的属性提供值(下面会讲到)

Spring 容器采用反射扫描的发现机制,在探测到 Spring 容器中有一个 org.springframework.beans.config.PropertyPlaceholderConfigurer 的 Bean 就会停止对剩余 PropertyPlaceholderConfigurer 的扫描,

换句话说,即 Spring 容器仅允许最多定义一个 PropertyPlaceholderConfigurer 或 <content:property-placeholder> 其余的会被 Spring 忽略

由于 Spring 容器只能有一个 PropertyPlaceholderConfigurer,如果有多个属性文件,这时就看谁先谁后了,先的保留 ,后的忽略。

还有一种情况,是 Spring 自动注入 properties 文件中的配置:要自动注入 properties 文件中的配置,需要在 Spring 配置文件中添加 org.springframework.beans.factory.config.PropertiesFactoryBeanorg.springframework.beans.factory.config.PreferencesPlaceholderConfigurer 的实例配置

<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="locations">
        <list>
            <value> classpath*:application.properties</value>
        </list>
    </property>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
    <property name="properties" ref="configProperties" />
</bean>

在这个配置文件中我们配置了注解扫描,和 configProperties 实例和 propertyConfigurer 实例,这样我们就可以在 java 类中自动注入配置了

@Component
public class Test{
    @Value("#{configProperties['userName']}")
    private String userName;

    public String getUserName(){
        return userName;
    }

}

自动注入需要使用 @Value 这个注解,这个注解的格式 #{configProperties['userName']} 其中 configProperties 是我们在配置文件中配置的 bean 的 id, userName 是在配置文件中配置的项

在 Spring 中的 xml 中使用 > 标签导入配置文件时,想要导入多个 properties 配置文件,如下:

<context:property-placeholderlocation="classpath:db.properties " />
<context:property-placeholderlocation="classpath:zxg.properties " />

结果发现不行,第二个配置文件始终读取不到,后来发现 <context:property-placeholder> 标签在 Spring 配置文件中只能存在一份!!!Spring 容器是采用反射扫描的发现机制,通过标签的命名空间实例化实例,当 Spring 探测到容器中有一个 org.springframework.beans.factory.config.PropertyPlaceholderCVonfigurer 的 Bean 就会停止对剩余 PropertyPlaceholderConfigurer 的扫描,即只能存在一个实例。

<context:property-placeholder
       location=""
       file-encoding=""
       ignore-resource-not-found=""
       ignore-unresolvable=""
       properties-ref=""
       local-override=""
       system-properties-mode=""
       order=""
/>

那如果有多个配置文件怎么办呢?那就多个文件之间以“,”分隔,如下:

<context:property-placeholderlocation="classpath:db.properties,classpath:monitor.properties" />

值得注意的是:多个配置文件将依次加载,如果后一个文件中有和前面某一个文件中属性名是相同的,最终取的值是后加载的值。

Spring Boot 最佳实践 学习笔记