Spring Boot 应用开发过程中,经常会使用到外部配置文件,例如 application.properties。此时,一般会通过 @ConfigurationProperties 注解,将外部配置文件中的属性映射到 Java bean 中。在今天的文章中,我们将一块儿来学习如何使用 Configuration Processor 生成 JSON 格式的外部配置元数据;然后,我们会展示 IntelliJ IDEA 是如何通过生成的 JSON 元数据来实现属性自动补全的(autocomplete)。
当我们开发 Spring Boot 程序时,或使用其他第三方提供的 Spring Boot 组件时,肯定会遇到外部属性。但通常我们并不了解这些外部属性,某些情况下甚至不知道某些属性的存在。如果应用中使用到的外部属性能够有一个描述性的解释文件,肯定能够提高我们对使用组件或别人对我们提供的应用的理解和掌握。
Spring Boot 提供了 Configuration Processor 机制,来生成外部配置文件的描述性源信息。这种信息不但可以帮制使用者更好地使用外部属性,而且 IDE 等开发工具也可以读取这些信息,以提供自动补全等提高开发效率的功能。
01-生成配置元数据
需要用到如下依赖:
 <!-- tag::configuration processor[] --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-configuration-processor</artifactId>    <version>2.1.6.RELEASE</version>    <optional>true</optional></dependency><!-- end::configuration processor[] -->
   复制代码
 
首先,定义一个 DatabaseProperties 类,用来封装 database 开头的属性。
 @ConfigurationProperties(prefix = "database")public class DatabaseProperties {    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-4000.         */        private int port;        /** 省略构造器、getter/setter */    }
    private String username;    private String password;    private Server server;    /** 省略构造器、getter/setter */}
   复制代码
 
用于封装的 configuration-metadata.properties 内容如下:
 database.username=testdbdatabase.password=passworddatabase.server.ip=127.0.0.1database.server.port=8001
   复制代码
 
然后,创建一个应用类 DatabaseApplication,作为运行程序的容器。
 @SpringBootApplication@EnableConfigurationProperties(DatabaseProperties.class)@PropertySource("classpath:properties/configuration-metadata.properties")public class DatabaseApplication {    /** 忽略其他 */}
   复制代码
 
最后,创建测试类,测试是否正确加载外部属性。
 @SpringBootTest(classes = {DatabaseApplication.class})public class DatabasePropertiesIntegrationTest {    @Autowired    private DatabaseProperties properties;
    @Test    public void whenSimplePropertyQueriedThenReturnsPropertyValue() {
        assertThat(properties.getUsername()).isEqualTo("testdb");        assertThat(properties.getPassword()).isEqualTo("password");        assertThat(properties.getServer().getIp()).isEqualTo("127.0.0.1");        assertThat(properties.getServer().getPort()).isEqualTo(8001);
    }}
   复制代码
 
在 IntelliJ IDEA 中,如果 classpath 下包含了 configuration-processor,则会自动地在 target\classes\META-INF 下生成元数据文件 spring-configuration-metadata.json。其内容类似于:
 {  "groups": [    {      "name": "database",      "type": "self.samson.example.basic.DatabaseProperties",      "sourceType": "self.samson.example.basic.DatabaseProperties"    },    {      "name": "database.server",      "type": "self.samson.example.basic.DatabaseProperties$Server",      "sourceType": "self.samson.example.basic.DatabaseProperties",      "sourceMethod": "getServer()"    }  ],  "properties": [    {      "name": "database.password",      "type": "java.lang.String",      "sourceType": "self.samson.example.basic.DatabaseProperties"    },    {      "name": "database.server.ip",      "type": "java.lang.String",      "description": "The IP of the database server",      "sourceType": "self.samson.example.basic.DatabaseProperties$Server"    },    {      "name": "database.server.port",      "type": "java.lang.Integer",      "description": "The Port of the database server. The Default value is 443. The allowed values are in the range 400-4000.",      "sourceType": "self.samson.example.basic.DatabaseProperties$Server"    },    {      "name": "database.username",      "type": "java.lang.String",      "sourceType": "self.samson.example.basic.DatabaseProperties"    }  ],  "hints": []}
   复制代码
 
其中的属性解释如下:
02-IntelliJ IDEA 使用元数据实现代码提示
上述生成的元信息,IDE 也可读取到,并据此提供自动补全提示功能。例如:
若要使用 IntelliJ IDEA 实现自动提示自定义 properties 功能,同样需要 spring-boot-configuration-processor 在项目的 classpath 下。
如果我们在 spring-configuration-metadata.json 中加入如下内容,在我们使用对应的属性值时,IDE 会提示我们候选值。
 "hints": [  {    "name": "database.server.port",    values: [      {        "value": "8001",        "description": "8001 port"      },      {        "value": "8002",        "description": "8002 port"      }    ]  }]
   复制代码
 
其效果如图所示:
评论