常用配置文件-Java

什么是配置文件

通俗来说,配置文件就是程序在启动或运行时所需信息的文件。在不同类型的配置文件中,这些信息有不同的格式。

常用配置文件类型(基于 Java)

格式 主要使用场景 生态支持 简单性 可读性 复杂数据写入支持
Properties Java 应用的基础配置、简单键值对 Java 生态(原生支持,无需引入第三方库) 简单 中 低
YAML 复杂、层次化的应用配置(现在使用较多) DevOps 生态(Spring Boot、K8s) 一般 高 高
XML 遗留系统、复杂数据结构定义、SOAP 等 旧系统/企业级生态(Java EE,原生支持) 较难 差 高
JSON 前后端数据交互、API 通信 跨语言生态(Web/API) 一般 中 高
TOML 语义化强的配置文件 通用生态 一般 高 高

各格式详解

1. Properties:最基础的 Java 配置

这是 Java 最经典的配置格式,它使用简单的 key=value 键值对存储数据。

# 注释:以 # 或 ! 开头
data.name = Tom

# 使用 = 和 : 效果是一样的
data.age: 18

# 多行值:使用 \ 续行
key1 = Hello, \
       world

特点

优点

  1. 格式简单,键值对一目了然
  2. 无需第三方库,可以直接读取 properties 文件
  3. 性能好,解析时间短,内存占用小
  4. 支持注释(以 # 或 ! 开头)

缺点

  1. 无层级结构,只能通过 . 来模拟嵌套
  2. 无数据类型,全都是字符串,读取后需要自行转换类型
  3. 不支持列表/数组,只能通过逗号分隔字符串来变通,例如 server=1,2,3
  4. 容易重复,多层配置会导致大量前缀
  5. 不支持引用和继承,无法复用配置片段

读取方式(原生)

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;

2. YAML

YAML 主要的作用是实现多环境动态交换。

1. 外部化配置

将配置信息从代码里抽离到外部文件(application.yaml),如同一份代码在不同环境下运行时加载不同的配置值。

2. 配置加载优先级(从高到低)

  1. 命令行参数(--service.port=8080)
  2. 操作系统环境变量(SERVICE_PORT=8080)
  3. JAR 包外面的 application-{profile}.yaml
  4. JAR 包内部的 application-{profile}.yaml
  5. JAR 包内部的 application.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");
}

3. XML

基本格式

<?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>

特点

  1. 树形层级结构,可表达任意深度的嵌套
  2. 支持属性(<server port="8080"/>)和子元素两种方式
  3. 有 DTD/Schema 可以验证格式合法性
  4. 冗余度高,标签成对出现,文件体积大
  5. 解析相对较慢

优点

  1. 层级结构清晰,天然的树形结构
  2. 自描述性强
  3. Schema/DTD 验证可以定义格式规范
  4. 命名空间支持,可以在同一个文件中混用多个 XML 方言而不冲突
  5. 属性 + 子元素:两种数据表达方式,灵活性强
  6. 工具链成熟:XPath 查询、XSLT 转换、DOM/SAX/StAX 多种解析方式
  7. 跨语言兼容:几乎所有语言都有 XML 解析库

缺点

  1. 冗余度高:成对的开始/结束标签导致文件体积膨胀,同样的数据 XML 可能是 JSON 的 2~3 倍
  2. 可读性差:标签嵌套多了眼花缭乱,查找信息困难
  3. 解析慢:DOM 方式需要全量加载,大文件内存开销大
  4. 不适合手写:写起来繁琐,容易漏掉闭合标签
  5. 注释不能嵌套:<!-- <!-- --> --> 是非法的

读取方式(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);

4. JSON

格式

{
  "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
}

特点

优点

缺点

读取方式

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());
}

5. TOML

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"

特点

优点

  1. 语义化强,格式明确统一,歧义少
  2. 可读性好,接近 INI/Properties 的简洁风格
  3. 支持注释
  4. 支持复杂数据结构(表、数组表、内联表)
  5. 类型丰富,且类型判断清晰

缺点

  1. 生态相对较新,支持的语言和库不如 JSON/XML 全面
  2. Java 原生不支持,需要引入第三方库(如 tomlj)
  3. 某些复杂表达(如多行字符串)语法略显繁琐

读取方式(以 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();