一、 简介
遇到很多次别人处理的项目,测试环境,本地开发和线上环境的配置不一样,每一次部署都要重新修改配置文件,提交审核代码,才能打包,非常不方便。
其实相信很多人都知道可以使用maven来做配置分离,不过通过maven来做配置分离也有多种方式,一种是通过filter,一种是通过配置文件拷贝。
filter这种方式看很多文章感觉作者理解是有误的,并且这种在IDE中开发的时候就不好用了,所以推荐配置文件拷贝的方式。
下面就来介绍一下这两种方式。
二、 环境介绍
我们先看一下项目目录:
config-dev.properties
jdbc.url = jdbc:mysql://127.0.0.1:3306/mybatis?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8
config-pro.properties
jdbc.url = jdbc:mysql://192.168.0.2:3308/mybatis?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8
jdbc.properties
jdbc.url=${jdbc.url}
为了简化问题,这里就只配置一个最简的配置,spring-datasource.xml中的DataSource加载的就是jdbc.properties文件中的配置。
三、 通过filter来分离配置
我们知道在maven构建(build)过程中有一个拷贝资源的阶段,这个可以通过resources标签配置。如果不知道,那么现在就把他当做一个真理。 resources是在build标签下,我们可以看一下resources配置。
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>config-pro.properties</exclude>
<exclude>config-dev.properties</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>jdbc.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
上面的配置了2个resource标签,第一个resource的意思就是把src/main/resources目录下的全部文件拷贝到classpath下,一般打包后就是在和src同一级。当然要除了excludes中配置的config-pro.properties和config-dev.properties这两个文件。
第二个resource配置的就是拷贝jdbc.properties这个文件,为什么前面配置了拷贝全部,后面还要单独配置一个呢?是因为我们要对这个文件执行filter。我们看到filtering标签设置为true就是这个目的。
那么问题来了,什么事filtering呢? 我们前面知道jdbc.properties的配置包含占位符,如下:
jdbc.url=${jdbc.url}
filtering就是处理配置文件中的占位符的。
那么配置文件中的占位符又是在什么地方配置的呢?
我们可以看到filters标签:
<filters>
<filter>src/main/resources/config-dev.properties</filter>
</filters>
filters标签中就是配置配置文件中占位符的配置文件,可以理解为配置文件的配置文件。感觉有点绕。不过没有关系,我们来看一下完整的pom配置。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.freemethod</groupId>
<artifactId>maven-split</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>config-pro.properties</exclude>
<exclude>config-dev.properties</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>jdbc.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<finalName>split</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<configuration>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</configuration>
</plugin>
<!--<plugin>-->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<!--<artifactId>maven-dependency-plugin</artifactId>-->
<!--<version>2.8</version>-->
<!--<executions>-->
<!--<execution>-->
<!--<id>copy-dependencies</id>-->
<!--<phase>package</phase>-->
<!--<goals>-->
<!--<goal>copy-dependencies</goal>-->
<!--</goals>-->
<!--<configuration>-->
<!--<outputDirectory>${project.build.directory}/lib</outputDirectory>-->
<!--</configuration>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
</plugins>
</build>
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<filters>
<filter>src/main/resources/config-dev.properties</filter>
</filters>
</build>
</profile>
<profile>
<id>pro</id>
<build>
<filters>
<filter>src/main/resources/config-pro.properties</filter>
</filters>
</build>
</profile>
</profiles>
</project>
我把其他的配置都删除了,只留下了build相关的配置。
这里说一下profile,这个可以在构建命令中指定profile的id来确定使用哪个profile。 activeByDefault标签标示默认使用的profile。
我们可以在使用下面的构建命令来使用id为pro的profile:
mvn package -Ppro
下面我们可以在项目目录下分别通过下面的命令来构建开发和生产环境的配置:
mvn package -Ppro
mvn package
构建完可以看到target目录如下:
把jar包的后缀修改一下,改为zip,然后查看可以看到jdbc.properties文件分别变为了:
jdbc.url = jdbc:mysql://127.0.0.1:3306/mybatis?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8
jdbc.url = jdbc:mysql://192.168.0.2:3308/mybatis?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8
filter方式还可以通过下面的方式配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.freemethod</groupId>
<artifactId>maven-split</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
</dependencies>
<build>
<filters>
<filter>src/main/resources/config-${env}.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>config-pro.properties</exclude>
<exclude>config-dev.properties</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>jdbc.properties</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<finalName>split</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<configuration>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</configuration>
</plugin>
<!--<plugin>-->
<!--<groupId>org.apache.maven.plugins</groupId>-->
<!--<artifactId>maven-dependency-plugin</artifactId>-->
<!--<version>2.8</version>-->
<!--<executions>-->
<!--<execution>-->
<!--<id>copy-dependencies</id>-->
<!--<phase>package</phase>-->
<!--<goals>-->
<!--<goal>copy-dependencies</goal>-->
<!--</goals>-->
<!--<configuration>-->
<!--<outputDirectory>${project.build.directory}/lib</outputDirectory>-->
<!--</configuration>-->
<!--</execution>-->
<!--</executions>-->
<!--</plugin>-->
</plugins>
</build>
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>pro</id>
<properties>
<env>pro</env>
</properties>
</profile>
</profiles>
</project>
可以自己感受一下这2中配置的区别。
四、 通过拷贝资源文件来分离
通过filter有一个问题就是,本地IDE中启动配置文件中的占位符不会处理,因为没有执行构建过程,和maven相关的命令。
所以可以通过拷贝文件来处理。
这个是什么意思呢?
就是一个环境对一个配置文件,执行maven构建过程处理resource的时候,拷贝指定的文件就可以了。
下面就是拷贝资源方式的项目结构:
在这种模式下,我们需要新建文件夹,因为我们需要文件名字一样。所以我们建立了一个dev和pro文件夹里面存放的方便是开发环境和生成环境的,如果需要测试环境的只需要再建立一个文件夹存放测试环境的配置文件就可以了。注意文件名字保持一致。
接下来我们就来看一下怎样配置pom文件了:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>config-pro.properties</exclude>
<exclude>config-dev.properties</exclude>
<exclude>jdbc.properties</exclude>
<exclude>dev/*</exclude>
<exclude>pro/*</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources/${env}</directory>
<includes>
<include>jdbc.properties</include>
</includes>
</resource>
</resources>
<finalName>split</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<configuration>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>pro</id>
<properties>
<env>pro</env>
</properties>
</profile>
</profiles>
需要注意的地方就是我们第一个resource把 jdbc.properties配置文件排除了。
第二个resource:
<resource>
<directory>src/main/resources/${env}</directory>
<includes>
<include>jdbc.properties</include>
</includes>
</resource>
我们可以看到使用了一个${env},这个我们不需要像filter在其他文件中配置,可以直接配置在profile中:
<profiles>
<profile>
<id>dev</id>
<properties>
<env>dev</env>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>pro</id>
<properties>
<env>pro</env>
</properties>
</profile>
</profiles>
看上面的profiles的配置,每一个profile中都一个一个properties的标签里面有一个env标签,env标签的值就是上面${env}的引用。
所以当我们使用:
mvn package -Pdev
打包的时候就会拷贝dev文件夹下的 jdbc.properties到指定目录下(一般是src同级目录,看文件结构,这里就是src同级目录,也就是包含在classpath中)
如果使用:
mvn package -Ppro
打包就会拷贝pro文件加下的 jdbc.properties到指定目录下(一般是src同级目录,看文件结构,这里就是src同级目录,也就是包含在classpath中)
注意我们directory指定的目录拷贝的目标目录都是到src目录下,如果要拷贝文件夹就得指定为上级目录 另外上面的方式还可以优化一下,因为我们的dev配置和本地一般是一样的,所以我们不需要建立dev文件夹。我们不需要排除jdbc.properties文件了就是把
<exclude>jdbc.properties</exclude>
然后添加一个拷贝资源覆盖模式的配置:
<properties>
<maven.resources.overwrite>true</maven.resources.overwrite>
</properties>
这样就实现了默认使用jdbc.properties文件,当打包的时候就会使用指定文件覆盖jdbc.properties文件。
另外不仅仅是package阶段,只有包含resources阶段的就会执行,例如:
mvn install
五、 总结
所以通过maven分离配置,这里有2中方式:
- 通过filters的filter指定配置文件的配置文件,然后配置resource将配置文件拷贝是的filtering设置为true。
- 为不同配置建立不同的目录,打包时候拷贝指定的配置文件就可以了