1. 概述

在开发 Spring Boot 应用时,我们经常需要将配置项绑定到 Java Bean 上,也就是常说的 @ConfigurationProperties 用法。但问题来了:如何让这些配置项变得可发现、可理解?

比如,某个配置有没有默认值?取值范围是什么?是否已废弃?光靠代码或文档很难快速搞清楚。

本文要介绍的就是 Spring Boot 提供的 配置元数据(Configuration Metadata)机制,它能自动生成结构化描述文件,让 IDE 能智能提示、自动补全配置项,极大提升开发体验。

核心工具是 Spring Boot 官方提供的 Configuration Processor,配合生成的 spring-configuration-metadata.json 文件,实现配置的“自我描述”。

2. 配置元数据简介

配置元数据是什么?
它是一个 JSON 格式的描述文件(spring-configuration-metadata.json),记录了项目中所有通过 @ConfigurationProperties 暴露的配置项信息,包括:

  • 配置项名称(如 database.username
  • 数据类型
  • 所属类来源
  • 默认值
  • 描述说明(来自 JavaDoc)
  • 取值约束(如 @Min, @Max

⚠️ 为什么重要?
因为主流 IDE(IntelliJ IDEA、Eclipse、VS Code)都支持读取这个文件,在 application.ymlapplication.properties 中输入配置时,会自动弹出提示,避免拼错、记错。

举个例子:

database:
  username: baeldung
  server:
    ip: 127.0.0.1
    port: 3306

当你输入 database. 时,IDE 就能提示你有哪些子配置可选,就像调用对象方法一样直观。

3. 引入依赖

要生成配置元数据,必须引入 Spring Boot 官方提供的注解处理器:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>3.1.5</version>
    <optional>true</optional>
</dependency>

关键点说明:

  • optional=true:表示该依赖仅在编译时使用,不会传递到其他依赖该项目的模块中。这是推荐做法,避免污染下游。
  • ✅ 注解处理器会在编译期扫描 @ConfigurationProperties 类,自动生成元数据文件。
  • ❌ 如果不加这个依赖,虽然 @ConfigurationProperties 仍可用,但不会有元数据生成,IDE 也无法提示。

4. 配置属性示例

我们以数据库配置为例,定义一个配置类:

@Configuration
@ConfigurationProperties(prefix = "database")
public class DatabaseProperties {
    
    public static class Server {
        private String ip;
        private int port;

        // standard getters and setters
        public String getIp() { return ip; }
        public void setIp(String ip) { this.ip = ip; }
        public int getPort() { return port; }
        public void setPort(int port) { this.port = port; }
    }
    
    private String username;
    private String password;
    private Server server;

    // standard getters and setters
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public Server getServer() { return server; }
    public void setServer(Server server) { this.server = server; }
}

对应的配置文件 databaseproperties-test.properties

# 简单属性
database.username=baeldung
database.password=password
# 嵌套属性
database.server.ip=127.0.0.1
database.server.port=3306

写个测试验证绑定是否正确:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AnnotationProcessorApplication.class)
@TestPropertySource("classpath:databaseproperties-test.properties")
public class DatabasePropertiesIntegrationTest {

    @Autowired
    private DatabaseProperties databaseProperties;

    @Test
    public void whenSimplePropertyQueriedThenReturnsPropertyValue() {
        Assert.assertEquals("Incorrectly bound Username property", 
            "baeldung", databaseProperties.getUsername());
        Assert.assertEquals("Incorrectly bound Password property", 
            "password", databaseProperties.getPassword());
    }
    
    @Test
    public void whenNestedPropertyQueriedThenReturnsPropertyValue() {
        Assert.assertEquals("Incorrectly bound Server IP nested property",
            "127.0.0.1", databaseProperties.getServer().getIp());
        Assert.assertEquals("Incorrectly bound Server Port nested property", 
            3306, databaseProperties.getServer().getPort());
    }
}

⚠️ 踩坑提醒:

  • 内部类 Server 必须有 getter/setter
  • 外部类中 server 字段也必须有 getter/setter,否则元数据无法识别嵌套结构

5. 生成配置元数据

编译项目后(执行 mvn compile 或 IDE 构建),会在以下路径生成元数据文件:

target/classes/META-INF/spring-configuration-metadata.json

内容如下:

{
  "groups": [
    {
      "name": "database",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceMethod": "getServer()"
    }
  ],
  "properties": [
    {
      "name": "database.password",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server.ip",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
    },
    {
      "name": "database.server.port",
      "type": "java.lang.Integer",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "defaultValue": 0
    },
    {
      "name": "database.username",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    }
  ],
  "hints": []
}

5.1 添加文档和约束提升元数据质量

光有字段名不够直观。我们可以通过 JavaDoc + JSR-303 校验注解 来丰富元数据信息。

修改 Server 类:

public static class Server {

    /**
     * The IP of the database server
     */
    private String ip;

    /**
     * The Port of the database server.
     * The Default value is 443.
     * The allowed values are in the range 400-800.
     */
    @Min(400)
    @Max(800)
    private int port = 443;

    // getters and setters
}

重新编译后,元数据文件中 database.server.ipport 会自动带上描述和默认值:

{
  "name": "database.server.ip",
  "type": "java.lang.String",
  "description": "The IP of the database server",
  "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
},
{
  "name": "database.server.port",
  "type": "java.lang.Integer",
  "description": "The Port of the database server. The Default value is 443.\n        The allowed values are in the range 400-800",
  "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
  "defaultValue": 443
}

✅ 效果:

  • IDE 中输入 database.server.port 时会显示完整描述
  • 显示默认值 443
  • 取值范围限制也会提示

💡 小技巧:
在 IntelliJ 中,如果修改了 JavaDoc 但元数据没更新,记得手动触发 Build → Rebuild Project,否则注解处理器不会重新运行。

5.2 元数据文件结构解析

生成的 spring-configuration-metadata.json 主要由三部分组成:

groups:配置分组

  • 用于组织逻辑相关的配置项
  • 通常对应 @ConfigurationPropertiesprefix
  • 示例中 databasedatabase.server 都是 group
{
  "name": "database",
  "type": "com.example.DatabaseProperties",
  "sourceType": "com.example.DatabaseProperties"
}

properties:具体配置项

  • 每个可配置的字段都会出现在这里
  • 包含类型、默认值、描述、来源类等信息
  • 是 IDE 自动补全的主要数据源

hints:提示信息(进阶)

  • 为某些配置项提供“建议值”及其说明
  • 比如日志级别可以是 INFO, DEBUG, WARN,每个值加个描述
  • IDE 会以下拉提示形式展示

示例(手动添加 additional-spring-configuration-metadata.json):

{
  "hints": [
    {
      "name": "database.log-level",
      "values": [
        {
          "value": "INFO",
          "description": "Standard level, suitable for production."
        },
        {
          "value": "DEBUG",
          "description": "Detailed logging, useful for troubleshooting."
        }
      ]
    }
  ]
}

📌 注意:hints 不会由注解处理器自动生成,需手动创建 additional-spring-configuration-metadata.json 文件补充。

6. 总结

通过本文你应该掌握了:

  • ✅ 使用 spring-boot-configuration-processor 自动生成配置元数据
  • ✅ 如何通过 JavaDoc 和校验注解丰富元数据内容
  • ✅ 元数据文件的结构(groups, properties, hints)
  • ✅ IDE 如何利用元数据提升开发效率

这套机制特别适合用于开发 Starter 组件内部中间件,让使用者无需翻源码就能清楚知道有哪些配置可用。

所有示例代码已托管至 GitHub:https://github.com/baeldung/tutorials
路径:spring-boot-modules/spring-boot-autoconfiguration


原始标题:A Guide to Spring Boot Configuration Metadata