Java中配置文件占位符替换的几种模式探讨与分析

背景

在Java/Maven项目中, 经常会有配置文件中配置项, 需要根据环境进行不同的参数变量替换, 如果搞不清楚, 经常会混淆.
当前有如下几种方式:

  • 编译期替换
  • 运行期替换
    • SpringBoot Profile
  • 其他替换
    • Docker Java应用启动参数

各有优缺点.

1. 编译期替换

1.1 Maven Profile

使用方式

方式1: 默认激活

<profiles>
  <profile>
    <id>apple</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
      <fruit>APPLE</fruit>
    </properties>
  </profile>
  <profile>
    <id>banana</id>
    <properties>
      <fruit>BANANA</fruit>
    </properties>
  </profile>
</profiles>
mvn initialize
mvn clean compile -DskipTests

方式2: 指定profile激活

mvn initialize -Pbanana
mvn clean compile -DskipTests -Pbanana

方式3: 自动激活Profile

如下根据maven内置的系统变量进行, 更多系统变量

<profile>
   <id>mac</id>
   <activation>
       <activeByDefault>false</activeByDefault>
       <os>
           <family>mac</family>
       </os>
   </activation>
   <properties>
       <database.driverClassName>org.postgresql.Driver</database.driverClassName>
       <database.url>jdbc:postgresql://localhost/database</database.url>
       <database.user>username</database.user>
       <database.password>password</database.password>
   </properties>
</profile>
<profile>
   <id>unix</id>
   <activation>
       <activeByDefault>false</activeByDefault>
       <os>
           <family>unix</family>
       </os>
   </activation>
   <properties>
       <database.driverClassName>com.mysql.jdbc.Driver</database.driverClassName>
       <database.url>jdbc:mysql://localhost:3306/database</database.url>
       <database.user>username</database.user>
       <database.password>password</database.password>
   </properties>
</profile>

配置文件变量替换

如下, 根据Maven Profile来选择不同的filter文件, 根据不同的filter文件, 编译期将 src/main/resources/* 中的变量都替换掉.

<project>
...
<build>
    <filters>
        <filter>src/main/filters-${active.profile}.properties</filter>
    </filters>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <active.profile>dev</active.profile>
        </properties>
        <!-- 把当前profile设置为默认profile,可以同时这是多个为默认-->
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <active.profile>test</active.profile>
        </properties>
    </profile>
    <profile>
        <id>product</id>
        <properties>
            <active.profile>product</active.profile>
        </properties>
    </profile>
...
</project>

1.2 autoconfig

本质其实与maven filter类似. 不做过多解释. 参见: Webx使用Autoconfig总结

2. 运行期替换

2.1 SpringBoot Profile

初步使用方式参见: 使用Profiles
具体多模块化多层次配置参见: SpringBoot+Maven多模块项目测试最佳实践
优点: 无需重新编译打包, 直接指定不同的运行参数即可.

3. 其他形式替换

如上, Maven Profile方案, 如果需要dev/pre/prod不同环境打出来不同的包, 需要在maven编译时手动指定 -Pdev.
但在实际CI/CD中, 不可能手动敲命令来打包/指定参数. 因此都需要与CI/CD框架进行结合, 从而感知到不同环境参数.

3.1 自定义部署脚本

这个依赖于CI/CD框架本身.

  • 在Jenkins里, 可以手动为不同的pipeline(环境), 指定不同的命令. dev pipeline指定-Pdev
  • 在其他框架里, 手动编写启动脚本等, 识别CI/CD框架的环境变量来指定-Pdev等参数

3.2 Docker Java应用启动参数

本质还是在Dockerfile中, 与CI/CD框架结合.

3.2.1 Dockerfile里指定环境变量, 直接在 Spring Properties文件里使用

例如: Dockerfile: Environment Variables

spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
FROM maven:3.6.3-openjdk-16-slim AS build
ENV DB_HOST=db \
     DB_USERNAME=username \
     DB_PASSWORD=password
COPY ./mobile-app-ws /usr/local/mobile-app-ws
WORKDIR /usr/local/mobile-app-ws/
RUN mvn -Dmaven.test.skip=true clean package

FROM tomcat:8
COPY --from=build /usr/local/mobile-app-ws/target/mobile-app-ws-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/mobile-app-ws.war
EXPOSE 8080
CMD ["catalina.sh", "run"]

基于不同的参数启动镜像: (注意ENV变量不在构建镜像时生效)

docker run -d --name ws-myappv3 \
   -e DB_HOST=db-myapp \
   -e DB_USERNAME=charith \
   -e DB_PASSWORD=charith \
   -p 38083:8080 \
   --network=myapp \
   myapp:v3