properties
导学
本节学习目标:
- 理解
properties元素在 MyBatis 全局配置文件中的定位与作用 - 掌握外部
.properties文件引入的两种方式(resource与url) - 弄清 Java 代码传入 Properties、外部文件、配置文件内嵌属性三者的优先级关系
- 能够在实际项目中正确运用
${...}占位符实现配置外部化
定义
在 MyBatis 全局配置文件 mybatis-config.xml 中,数据库连接信息(驱动、URL、用户名、密码)如果直接写死,会导致:
- 不同环境(开发、测试、生产)需要维护多份 XML 文件
- 密码等敏感信息暴露在版本控制中
- 运维人员修改配置必须重新打包应用
properties 元素的核心使命是引入外部属性文件,实现配置外部化,让 XML 中可以使用 ${driver}、${url} 等占位符,从而将易变配置抽离到外部文件或通过 Java 代码动态注入。
适用位置与核心属性
properties 必须是 mybatis-config.xml 中第一个子元素(MyBatis 3.5.x 的 DTD 约束)。
| 属性 | 是否必填 | 说明 |
|---|---|---|
resource | 与 url 二选一 | 从类路径(classpath)加载属性文件,如 db.properties |
url | 与 resource 二选一 | 从绝对 URL 加载属性文件,如 file:///opt/config/db.properties |
此外,还可以在 properties 标签体内直接定义若干 property 子标签:
<properties resource="db.properties">
<property name="username" value="root"/>
</properties>
核心原理
配置加载优先级流程
MyBatis 在解析 properties 时,会按照严格优先级合并三处来源的属性,高优先级覆盖低优先级:
优先级从高到低:
- Java 代码中通过
SqlSessionFactoryBuilder.build(inputStream, properties)传入的Properties resource或url指向的外部.properties文件properties标签体内嵌的property子标签
源码层面:MyBatis 使用
XMLConfigBuilder.propertiesElement(XNode)方法完成上述合并逻辑,最终存储在Configuration对象的variables字段中。
完整示例
场景说明
乐途公司员工管理系统需要连接 MySQL 5.7 数据库 employee_db。开发环境、测试环境的数据库密码不同,要求将连接配置抽离到外部文件,同时演示 Java 代码动态覆盖密码的场景。
操作前的状态
项目目录结构:
src/main/resources/
├── mybatis-config.xml
└── db.properties
db.properties 内容:
# 乐途公司员工数据库配置
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/employee_db?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
username=root
password=dev_password_123
完整配置代码
mybatis-config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 第一步:引入外部属性文件 -->
<properties resource="db.properties">
<!-- 内嵌属性,优先级最低,仅作为默认值 -->
<property name="username" value="fallback_user"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 使用 ${...} 占位符引用外部属性 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
Java 代码动态覆盖(生产环境传入真实密码):
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
import java.util.Properties;
public class MyBatisBootstrap {
public static void main(String[] args) throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 动态覆盖:最高优先级
Properties props = new Properties();
props.setProperty("password", "prod_secret_456");
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(inputStream, props);
System.out.println("SqlSessionFactory 创建成功,密码已被动态覆盖");
}
}
实际效果/结果
运行后,MyBatis 实际使用的连接参数为:
| 参数 | 最终值 | 来源说明 |
|---|---|---|
| driver | com.mysql.cj.jdbc.Driver | db.properties |
| url | jdbc:mysql://localhost:3306/employee_db?... | db.properties |
| username | root | db.properties(覆盖内嵌默认值) |
| password | prod_secret_456 | Java 代码传入,最高优先级 |
控制台输出:
SqlSessionFactory 创建成功,密码已被动态覆盖
分析
${driver}等占位符在XMLConfigBuilder解析阶段被替换为真实值- 若
db.properties中缺少某个 key,而 XML 内嵌标签有定义,则回退到内嵌值 - 若三处都未定义,MyBatis 不会报错,而是将
${xxx}原样保留,最终在创建连接时失败
易错场景/常见误区
| 误区 | 错误表现 | 正解 |
|---|---|---|
properties 放错位置 | 将 properties 写在 environments 之后,启动报 DTD 约束错误 | properties 必须是 configuration 的第一个子元素 |
| 占位符拼写错误 | 写成 ${dirver},启动不报错但连接时提示 No suitable driver found | 仔细核对 .properties 文件中的 key 名与 XML 中 ${} 内名称完全一致 |
| 认为 XML 内嵌属性优先级最高 | 内嵌属性被外部文件覆盖,导致使用了错误的用户名 | 记住优先级:Java 代码 > 外部文件 > 内嵌属性 |
使用 url 属性时写相对路径 | url="db.properties" 找不到文件 | url 要求绝对路径(file:/// 或 http://),相对路径请用 resource |
| 外部文件编码问题 | 中文注释导致解析乱码,属性值读取错误 | 统一使用 UTF-8 编码保存 .properties 文件 |
面试考点
Q1:MyBatis 中 properties 元素的加载优先级是怎样的?
A:从高到低依次为:① Java 代码通过
SqlSessionFactoryBuilder.build()传入的Properties对象;②resource或url引入的外部.properties文件;③properties标签体内嵌的property子标签。高优先级覆盖低优先级。
Q2:如果 ${password} 在三处都没有定义,MyBatis 会报错吗?
A:解析阶段不会报错,
XMLConfigBuilder会原样保留${password}字符串。直到dataSource创建连接时,由于密码为${password}而导致连接失败,此时才会抛出异常。因此建议在启动后打印关键配置做校验。
Q3:resource 和 url 有什么区别?
A:
resource从类路径(classpath)加载,适合打包在 jar 内的配置文件;url从绝对 URL 加载,适合配置文件放在服务器固定目录或远程 HTTP 地址的场景。日常开发推荐resource。
小结
properties 是 MyBatis 全局配置文件的"入口第一站",它通过外部化配置解决了环境差异与敏感信息暴露的问题。掌握三处来源的优先级顺序,是避免配置被意外覆盖的关键。配合 ${...} 占位符,可以让 mybatis-config.xml 成为一份与环境无关的"纯净模板"。
下一章引子
配置外部化之后,MyBatis 的行为还受大量全局开关控制——比如是否开启二级缓存、是否开启驼峰映射、是否使用 JDBC 自动生成主键。这些开关全部集中在 settings 元素中,它是影响 MyBatis 运行时的"总控面板"。接下来,我们将逐一拆解这些关键设置。