今天为什么要讲这个插件,它其实挺重要的,现在很多项目也在使用这个插件去做多环境的文件配置,如果一不小心就容易造成线上事故,你会发现你本地代码里面明明改了某个资源文件,但是到了其它环境就不生效,原因是你忽略了项目的打包机制。下面以一个案例讲解一下它的作用。
首先我的项目是一个使用ssm框架的多模块项目,使用jetty容器部署web服务。父pom文件打包配置如下:
<build>
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<!-- <excludes>
<exclude>**/disconf.properties</exclude>
</excludes>-->
</resource>
<resource>
<directory>src/main/assembly/${env.devMode}/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/disconf.properties</exclude>
</excludes>
</resource>
</resources>
</build>
里面引进了Maven Assembly 插件。项目打包的时候首先会打包src/main/resources资源下的所有文件,但排除disconf.properties。
后面又会再打包一次src/main/assembly/${env.devMode}/resources目录下的资源文件,并排除该目录下的disconf.properties文件,这个时候假如该目录下有一个a.xml文件和之前的打包的文件重名了,那么会覆盖掉前面的a.xml。这样的好处就是可以实现定制化配置,比如a.xml可能是你本地适用的一个文件,到了生产环境你的a.xml配置可能不一样,你就可以定义在assembly目录下,生产打包就会覆盖掉之前的文件。
然后下面是我的web模块(resource资源存在该模块下)的pom文件打包配置如下:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly-dev</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<skipAssembly>false</skipAssembly>
<descriptors>
<!--<descriptor>src/main/assembly/${env.devMode}/assembly.xml</descriptor>-->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
<finalName>${project.artifactId}</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env.devMode>local</env.devMode>
<skipAssemblyDEV>false</skipAssemblyDEV>
<skipAssemblySIT>true</skipAssemblySIT>
<skipAssemblyPROD>true</skipAssemblyPROD>
</properties>
</profile>
<profile>
<id>dev</id>
<activation>
</activation>
<properties>
<env.devMode>dev</env.devMode>
<skipAssemblyDEV>false</skipAssemblyDEV>
<skipAssemblySIT>true</skipAssemblySIT>
<skipAssemblyPROD>true</skipAssemblyPROD>
</properties>
</profile>
<profile>
<id>sit</id>
<activation>
</activation>
<properties>
<env.devMode>sit</env.devMode>
<skipAssemblyDEV>true</skipAssemblyDEV>
<skipAssemblySIT>false</skipAssemblySIT>
<skipAssemblyPROD>true</skipAssemblyPROD>
</properties>
</profile>
<profile>
<id>prod</id>
<activation>
</activation>
<properties>
<env.devMode>prod</env.devMode>
<skipAssemblyDEV>true</skipAssemblyDEV>
<skipAssemblySIT>true</skipAssemblySIT>
<skipAssemblyPROD>false</skipAssemblyPROD>
</properties>
</profile>
</profiles>
</build>
其中结合Maven 的 Profile 配置 ,用于在不同环境(本地开发、开发环境、测试环境、生产环境)之间切换,控制构建行为。每个 profile 定义了一个环境,并设置三个 skipAssemblyXXX 参数,控制是否跳过对应环境的打包(assembly)操作。确保每个环境打包自己的assembly下的资源文件。
项目实际目录结构示例如下:
src/main/
├── resources/ # 公共资源
│ ├── application.properties
│ └── logback.xml
└── assembly/
├── local/resources/ # 本地环境专用
│ └── application.properties
├── dev/resources/ # 开发环境专用
│ └── application.properties
├── sit/resources/ # 测试环境专用
│ └── application.properties
└── prod/resources/ # 生产环境专用
└── application.properties
其中以sit下的asseembly文件为例:
<assembly>
<id>sit</id>
<formats>
<format>war</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/webapp</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>target/classes</directory>
<excludes>
<exclude>**/sysdemo.properties</exclude>
<exclude>**/logback.xml</exclude>
<exclude>**/jdbc.properties</exclude>
<exclude>**/disconf.properties</exclude>
<exclude>**/springmvc/spring-mvc.xml</exclude>
<exclude>**/spring/spring-context.xml</exclude>
<exclude>**/demo.properties</exclude>
<exclude>**/machineMaskdemo.properties</exclude>
</excludes>
<outputDirectory>/WEB-INF/classes</outputDirectory>
</fileSet>
<fileSet>
<directory>src/main/assembly/sit/resources</directory>
<outputDirectory>WEB-INF/classes</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<useProjectArtifact>false</useProjectArtifact>
<outputDirectory>WEB-INF/lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
该文件定义了一个打包机制,打包 target/classes 中的编译文件到 /WEB-INF/classes时排除了多个环境相关配置文件,这个时候就需要特别注意,比如里面的spring-context.xml文件,该文件被排除了,也就是打包的时候不会把resources目录下的文件打包。
但是这里有个疑问,既然/assembly/sit/resources/spring/spring-context.xml 文件会覆盖之前的(所以改动该目录下的spring-context.xml 文件才会在sit生效),为什么上面还要排除?这里排除的好处是可以避免重复打包 - 减少不必要的文件处理。
同时值得注意的是disconf.properties文件被排除,然后会使用/assembly下配置的disconf.properties文件。disconf是一个分布式文件配置中心,它也可以控制不同环境的文件配置,类似于nacos,但是它不能控制xml文件,所以才要结合assembly插件实现多环境配置文件的隔离。使用disconf.properties之后就可以不用在/assembly目录下定义被排除的其它properties文件了,因为我们可以放在disconf配置中心,实现远程读取,动态修改而不用重启服务。