资源过滤
本章是"属性与资源过滤"主题的开篇。理解资源过滤机制,是掌握 Maven 多环境构建、配置外部化的前提。它与下一章"Profile"配合使用,可以实现"同一份代码,不同环境运行"的目标。
核心机制
资源过滤是在构建时,将资源文件中的占位符替换为实际值的过程。Maven 通过 resources 标签和 filtering 开关控制这一行为,让同一份 application.properties 可以在开发、测试、生产环境中呈现不同的内容。
什么是资源过滤?
在 Java 项目中,配置文件(如 .properties、.yml、.xml)通常需要随环境变化。例如:
- 开发环境连接
localhost:3306/dev_db - 测试环境连接
test-db.feixiang.com:3306/test_db - 生产环境连接
prod-db.feixiang.com:3306/prod_db
如果没有资源过滤,开发者可能维护三份几乎相同的配置文件,或者手动修改后打包——既容易出错,也难以追溯。
资源过滤的解决思路是:在资源文件中用占位符(如 ${db.host})标记"会变的地方",构建时由 Maven 将占位符替换为真实值。
过滤的三要素
| 要素 | 作用 | 示例 |
|---|---|---|
| 资源文件 | 包含占位符的待处理文件 | src/main/resources/application.properties |
| 属性值来源 | 提供替换值的配置 | pom.xml 中的 <properties>、外部 .properties 文件、系统属性 |
| 过滤开关 | 决定是否执行替换 | <filtering>true</filtering> |
占位符的语法
Maven 默认使用 ${...} 作为占位符,与 Java 属性文件的语法一致:
# 原始资源文件(src/main/resources/config.properties)
db.host=${db.host}
db.port=${db.port}
db.name=${db.name}
app.version=${project.version}
构建后,这些 ${...} 会被替换为实际值。
生活类比:填空题试卷
想象老师(架构师白歌)出了一份填空题试卷(资源文件),上面有很多空:____(占位符)。
- 没有资源过滤:老师必须给每个班级(环境)单独印一份填好答案的试卷,三个班级印三份,改一处就要重印三份。
- 有资源过滤:老师只印一份带空白的试卷,考试时由监考老师(Maven)根据该班级的标准答案(属性值)现场填上。改答案只需改标准答案,不用重印试卷。
资源过滤的价值在于:将"会变的内容"与"不变的模板"分离。
图示
上图展示了资源过滤的完整流程:原始资源文件中的占位符,在构建时被 Maven 替换为实际值,最终输出到 target/ 目录。注意,只有 target/ 里的文件会被替换,src/ 里的原始文件始终保持不变——这是 Maven 的"只读源码"原则。
完整示例
场景
飞翔科技的 employee-system 项目需要连接数据库。架构师白歌要求:同一份代码在开发环境、测试环境、生产环境使用不同的数据库配置,且不允许在代码里硬编码环境差异。
操作前:项目状态
项目目录结构:
employee-system/
├── pom.xml
└── src/
└── main/
├── java/
│ └── com/feixiang/...
└── resources/
└── application.properties
src/main/resources/application.properties(原始文件,含占位符):
# 数据库配置(占位符将在构建时被替换)
db.host=${db.host}
db.port=${db.port}
db.name=${db.name}
db.username=${db.username}
db.password=${db.password}
# 应用信息(引用 POM 属性)
app.name=${project.name}
app.version=${project.version}
build.timestamp=${maven.build.timestamp}
pom.xml 初始状态(未开启过滤):
<?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>com.feixiang</groupId>
<artifactId>employee-system</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<db.host>localhost</db.host>
<db.port>3306</db.port>
<db.name>dev_db</db.name>
<db.username>dev_user</db.username>
<db.password>dev_pass</db.password>
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
</properties>
<build>
<!-- 尚未开启资源过滤 -->
</build>
</project>
操作步骤
步骤一:开启资源过滤
后端小崔在 pom.xml 的 <build> 段中配置 resources:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
步骤二:执行构建
mvn clean compile
步骤三:查看过滤结果
构建后,target/classes/application.properties 的内容:
# 数据库配置(占位符将在构建时被替换)
db.host=localhost
db.port=3306
db.name=dev_db
db.username=dev_user
db.password=dev_pass
# 应用信息(引用 POM 属性)
app.name=employee-system
app.version=1.0.0
build.timestamp=2024-01-15 09:30:00
过滤前后的资源文件对比
| 对比项 | 过滤前(src/main/resources) | 过滤后(target/classes) |
|---|---|---|
db.host | ${db.host} | localhost |
db.port | ${db.port} | 3306 |
db.name | ${db.name} | dev_db |
app.version | ${project.version} | 1.0.0 |
build.timestamp | ${maven.build.timestamp} | 2024-01-15 09:30:00 |
操作结果及分析
- 源码不变:
src/main/resources/application.properties中的占位符始终保留,方便版本控制 - 产物正确:
target/classes/application.properties已被替换为实际值,运行时读取的是产物中的文件 - 属性来源多样:
${project.version}和${project.name}是内置 POM 属性,${maven.build.timestamp}是 Maven 内置属性,自定义属性在<properties>中声明
易错点与常见问题
误区一:开启过滤后,所有文件都会被替换
错误认知:"我在 pom.xml 里写了 <filtering>true</filtering>,所以 src/main/resources 下所有文件都会被过滤。"
纠正:开启过滤后,文本文件(如 .properties、.xml、.yml)中的 ${...} 会被替换,但二进制文件(如 .jpg、.pdf、.zip)如果包含 ${ 字节序列,可能被意外破坏。正确的做法是将二进制资源单独配置,关闭其过滤:
<build>
<resources>
<!-- 文本资源:开启过滤 -->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>**/*.yml</include>
<include>**/*.xml</include>
</includes>
</resource>
<!-- 二进制资源:关闭过滤,避免损坏 -->
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.jpg</include>
<include>**/*.png</include>
<include>**/*.pdf</include>
</includes>
</resource>
</resources>
</build>
误区二:占位符没被替换,是因为属性没定义
典型问题:小崔发现构建后 ${db.host} 原样出现在产物中,没有被替换。
排查清单:
- 检查过滤开关:确认
<filtering>true</filtering>已声明 - 检查属性名拼写:
pom.xml中定义的是db.host,资源文件里写的是${db.host},注意大小写和拼写完全一致 - 检查属性作用域:在
<properties>中定义的属性是全局的;如果在<profile>中定义,需要激活对应 profile 才生效 - 检查资源目录路径:确认
<directory>指向的是实际存放资源文件的目录
误区三:用 @...@ 占位符不生效
背景:Spring Boot 项目常用 @propertyName@ 作为占位符,与 Maven 的 ${...} 区分。
纠正:Maven 原生只支持 ${...} 语法。如果需要使用 @...@,必须显式配置 Maven Resources 插件:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
</plugins>
</build>
小结
资源过滤是 Maven 实现"同一份代码,多环境运行"的基础机制。通过在资源文件中使用 ${...} 占位符,并在 pom.xml 中定义属性值,Maven 在构建时自动完成替换。核心要点:
- 用
<filtering>true</filtering>开启过滤 - 属性值可来自
<properties>、内置 POM 属性、外部过滤文件或系统属性 - 二进制资源应单独配置并关闭过滤,避免文件损坏
- 只有
target/中的产物被替换,src/中的源文件保持不变
本章与全局的关系:本章讲解了资源过滤的基本原理。下一章"Profile"将在此基础上,展示如何为不同环境(dev/test/prod)定义不同的属性值,实现真正的多环境构建。