乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

资源过滤

本章是"属性与资源过滤"主题的开篇。理解资源过滤机制,是掌握 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} 原样出现在产物中,没有被替换。

排查清单:

  1. 检查过滤开关:确认 <filtering>true</filtering> 已声明
  2. 检查属性名拼写:pom.xml 中定义的是 db.host,资源文件里写的是 ${db.host},注意大小写和拼写完全一致
  3. 检查属性作用域:在 <properties> 中定义的属性是全局的;如果在 <profile> 中定义,需要激活对应 profile 才生效
  4. 检查资源目录路径:确认 <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)定义不同的属性值,实现真正的多环境构建。

下一页
Profile