通俗来说,配置文件就是程序在启动或运行时所需信息的文件。在不同类型的配置文件中,这些信息有不同的格式。
| 格式 | 主要使用场景 | 生态支持 | 简单性 | 可读性 | 复杂数据写入支持 |
|---|---|---|---|---|---|
| Properties | Java 应用的基础配置、简单键值对 | Java 生态(原生支持,无需引入第三方库) | 简单 | 中 | 低 |
| YAML | 复杂、层次化的应用配置(现在使用较多) | DevOps 生态(Spring Boot、K8s) | 一般 | 高 | 高 |
| XML | 遗留系统、复杂数据结构定义、SOAP 等 | 旧系统/企业级生态(Java EE,原生支持) | 较难 | 差 | 高 |
| JSON | 前后端数据交互、API 通信 | 跨语言生态(Web/API) | 一般 | 中 | 高 |
| TOML | 语义化强的配置文件 | 通用生态 | 一般 | 高 | 高 |
这是 Java 最经典的配置格式,它使用简单的 key=value 键值对存储数据。
# 注释:以 # 或 ! 开头
data.name = Tom
# 使用 = 和 : 效果是一样的
data.age: 18
# 多行值:使用 \ 续行
key1 = Hello, \
world
特点
. 来模拟,例如 data.name优点
# 或 ! 开头)缺点
. 来模拟嵌套server=1,2,3读取方式(原生)
Properties props = new Properties();
try (InputStream in = new FileInputStream("config.properties")) {
props.load(in);
}
String name = props.getProperty("data.name");
在 Spring Boot 中使用
@PropertySource("config.properties")
@Value("${data.name}")
private String name;
YAML 主要的作用是实现多环境动态交换。
1. 外部化配置
将配置信息从代码里抽离到外部文件(application.yaml),如同一份代码在不同环境下运行时加载不同的配置值。
2. 配置加载优先级(从高到低)
--service.port=8080)SERVICE_PORT=8080)application-{profile}.yamlapplication-{profile}.yamlapplication.yaml格式
server:
port: 8080
host: localhost
# 列表配置:- 表示列表项
servlets:
- name: dispatcher
class: com.example.DispatcherServlet
load-on-startup: 1
- name: api
class: com.example.ApiServlet
引用与锚点
defaults: &defaults
timeout: 30
retry: 3
service-a:
<<: *defaults
url: http://a.example.com
特点
- 表示列表项# 注释& 和引用 * 减少重复优点
缺点
3. 取值方式
@Value:单个简单注入
@ConfigurationProperties:批量注入结构化配置
解析方法
Yaml yaml = new Yaml();
try (InputStream is = getClass().getClassLoader().getResourceAsStream("config.yml")) {
Map<String, Object> config = yaml.load(is);
Map<String, Object> server = (Map<String, Object>) config.get("server");
int port = (int) server.get("port");
}
基本格式
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<database>
<host>localhost</host>
<port>8080</port>
<credentials>
<username>root</username>
<password>secret</password>
</credentials>
</database>
</configuration>
特点
<server port="8080"/>)和子元素两种方式优点
缺点
<!-- <!-- --> --> 是非法的读取方式(DOM 方式,全量加载,适合小文件)
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new File("config.xml"));
Element root = doc.getDocumentElement(); // <configuration>
Element database = (Element) root.getElementsByTagName("database").item(0);
Element credentials = (Element) database.getElementsByTagName("credentials").item(0);
Element usernameElem = (Element) credentials.getElementsByTagName("username").item(0);
String username = usernameElem.getTextContent();
读取方式(XPath)
import javax.xml.xpath.*;
Document doc = builder.parse(new File("config.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
// 用路径表达式直接定位
String username = xpath.evaluate("/configuration/database/credentials/username", doc);
// 结果:"root"
// 或者用 // 从任意位置搜索
String username2 = xpath.evaluate("//credentials/username", doc);
// 结果:"root"
读取方式(DOM4J)
import org.dom4j.*;
import org.dom4j.io.*;
SAXReader reader = new SAXReader();
Document doc = reader.read(new File("config.xml"));
// 一行搞定
String username = doc.selectSingleNode("//credentials/username").getText();
// 结果:"root"
读取方式(SAX,事件驱动,适合大文件)
SAX 是一种基于事件驱动的流式解析方式,边读边解析,不将整个文档加载到内存,适合处理大文件。
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
DefaultHandler handler = new DefaultHandler() {
boolean inUsername = false;
// 遇到开始标签时触发
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("username".equals(qName)) {
inUsername = true;
}
}
// 遇到文本内容时触发
@Override
public void characters(char[] ch, int start, int length) {
if (inUsername) {
System.out.println("username = " + new String(ch, start, length));
}
}
// 遇到结束标签时触发
@Override
public void endElement(String uri, String localName, String qName) {
if ("username".equals(qName)) {
inUsername = false;
}
}
};
parser.parse(new File("config.xml"), handler);
格式
{
"server": {
"port": 8080,
"host": "localhost"
},
"database": {
"url": "jdbc:mysql://localhost:3306/mydb",
"username": "root",
"pool": {
"maxSize": 20,
"minSize": 5
}
},
"features": ["auth", "logging", "cache"],
"debug": false
}
特点
"_comment" 变通)优点
缺点
读取方式
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
// 读取为 Java 对象
ServerConfig config = mapper.readValue(
new File("config.json"), ServerConfig.class);
// 读取为 JsonNode(树模型,灵活访问)
JsonNode root = mapper.readTree(new File("config.json"));
int port = root.get("server").get("port").asInt();
String host = root.get("server").get("host").asText();
// 读取数组
JsonNode features = root.get("features");
for (JsonNode feature : features) {
System.out.println(feature.asText());
}
TOML 全称是 Tom's Obvious, Minimal Language,是一种语义化强、易读的配置文件格式。
格式
# 这是注释
title = "TOML 示例"
[server]
host = "localhost"
port = 8080
[database]
url = "jdbc:mysql://localhost:3306/mydb"
username = "root"
max-connections = 20
# 数组
features = ["auth", "logging", "cache"]
# 数组表(Array of Tables)
[[servers]]
name = "server-a"
ip = "10.0.0.1"
[[servers]]
name = "server-b"
ip = "10.0.0.2"
特点
[table] 表示层级(表),可嵌套[[array-of-tables]] 表示数组表# 注释max-connections)优点
缺点
读取方式(以 tomlj 为例)
import org.tomlj.Toml;
import org.tomlj.TomlParseResult;
import org.tomlj.TomlTable;
Path path = Paths.get("config.toml");
TomlParseResult result = Toml.parse(path);
// 读取基本类型
String host = result.getString("server.host");
long port = result.getLong("server.port");
// 读取数组
List<String> features = result.getArray("features").toStringList();