參考: https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor
Spring Boot Jars包含元數據文件,這些文件提供了所有支持的配置屬性的詳細信息。這些文件旨在讓IDE開發人員在使用application.properties或application.yml文件時提供提示及自動完成功能;當然開發人員也可以自定義元數據文件,這樣我們自己定義的配置屬性可以像系統的屬性一樣擁有提示功能,想想也是很美好的事情,接下來我們就看下如何實現這樣的功能。
1.配置處理器
通過使用spring boot提供的配置處理器jar包,你可以使用@ConfigurationProperties註釋輕鬆的生成自己的配置元數據文件,jar包包含一個Java註釋處理器,在編譯項目時調用它。要使用處理器,請引入對如下spring boot配置處理器的依賴關係:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
處理器同時獲取用@ConfigurationProperties註釋的類和方法。配置類中字段值的Javadoc用於填充description屬性。
只應將簡單文本與@ConfigurationProperties字段Javadoc一起使用,因爲在將它們添加到JSON之前不會對它們進行處理
屬性是通過標準getter和setter來發現的,他們對集合類型進行了特殊處理(即使只有getter存在,也會檢測到)。註釋處理器還支持使用@Data、@Getter、@Setter lombok註釋。
如果你正在使用AspectJ在你的項目中,你需要確保註釋處理器只運行一次;這裏有幾種方法處理,如果你使用的是maven,你可以顯式配置maven-apt-plugin,並僅在哪裏像註釋處理器添加依賴項。你可以讓AspectJ插件運行所有處理,並在maven編譯器插件配置中禁用註釋處理。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<proc>none</proc>
</configuration>
</plugin>
2.元數據格式
配置元數據文件在jar包的 META-INF/spring-configuration-metadata.json 裏,它們使用一種簡單的JSON格式,將項目分類在“groups”或“properties”下,將附加值提示分類在“hints”下,如下示例所示:
{"groups": [
{
"name": "server",
"type": "org.springframework.boot.autoconfigure.web.ServerProperties",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
},
{
"name": "spring.jpa.hibernate",
"type": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate",
"sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties",
"sourceMethod": "getHibernate()"
}
...
],"properties": [
{
"name": "server.port",
"type": "java.lang.Integer",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
},
{
"name": "server.address",
"type": "java.net.InetAddress",
"sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties"
},
{
"name": "spring.jpa.hibernate.ddl-auto",
"type": "java.lang.String",
"description": "DDL mode. This is actually a shortcut for the \"hibernate.hbm2ddl.auto\" property.",
"sourceType": "org.springframework.boot.autoconfigure.orm.jpa.JpaProperties$Hibernate"
}
...
],"hints": [
{
"name": "spring.jpa.hibernate.ddl-auto",
"values": [
{
"value": "none",
"description": "Disable DDL handling."
},
{
"value": "validate",
"description": "Validate the schema, make no changes to the database."
},
{
"value": "update",
"description": "Update the schema if necessary."
},
{
"value": "create",
"description": "Create the schema and destroy previous data."
},
{
"value": "create-drop",
"description": "Create and then destroy the schema at the end of the session."
}
]
}
]}
每個“property”都是用戶用給定值指定的配置項。例如可以在application.properties中指定server.port和server.address,如下所示:
server.port=9090
server.address=127.0.0.1
group是更高級別的項,它們本身並不指定值,而是爲屬性提供上線文分組。例如,server.port和server.address屬性是服務器組的一部分。
並不要求每一個property擁有一個group,一些properties可能單獨存在。
最後,hints是用於幫助用戶配置給定屬性的附加信息。例如,當開發人員配置spring.jpa.hibernate.ddl-auto屬性時,工具可以使用提示爲none、validate、update、create和create drop值提供一些自動完成幫助。
3.生成元數據文件示例
- 引入第一步中的maven依賴
- 定義一個使用@ConfigurationProperties註解標註的java類
該類包含普通的自動提示、枚舉、Map對應keyvalue提示、配置屬性值、屬性過期、引入外部類
package com.yaomy.control.common.control.conf;
import com.yaomy.control.common.control.test.School;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import java.util.Map;
/**
* 配置元數據文件.
*/
@SuppressWarnings("all")
@ConfigurationProperties(prefix = "spring.yaomy", ignoreInvalidFields = true)
public class MetaDataProperties {
private HttpClient httpClient = new HttpClient();
private Test test = new Test();
public HttpClient getHttpClient() {
return httpClient;
}
public Test getTest() {
return test;
}
/**
* HttpClient網絡請求屬性配置類
*/
public static class HttpClient{
/**
* HttpClientService read timeout (in milliseconds),default:5000
*/
private Integer readTimeOut = 5000;
/**
* HttpClientService connect timeout (in milliseconds),default:10000
*/
private Integer connectTimeOut = 10000;
public Integer getReadTimeOut() {
return readTimeOut;
}
public void setReadTimeOut(Integer readTimeOut) {
this.readTimeOut = readTimeOut;
}
public Integer getConnectTimeOut() {
return connectTimeOut;
}
public void setConnectTimeOut(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
}
/**
* test metadata
*/
public static class Test{
/**
* context
*/
private Map<String, String> context;
/**
* test tip
*/
private String testTip;
/**
* test type
*/
private Type testType = Type.ADD;
/**
* school
*/
@NestedConfigurationProperty
private School school;
/**
* enable or disable default false
*/
private Boolean testEnable = Boolean.FALSE;
/**
* deprecated test
*/
private String deprecated;
public Map<String, String> getContext() {
return context;
}
public void setContext(Map<String, String> context) {
this.context = context;
}
public String getTestTip() {
return testTip;
}
public void setTestTip(String testTip) {
this.testTip = testTip;
}
public Type getTestType() {
return testType;
}
public void setTestType(Type testType) {
this.testType = testType;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
public Boolean getTestEnable() {
return testEnable;
}
public void setTestEnable(Boolean testEnable) {
this.testEnable = testEnable;
}
@DeprecatedConfigurationProperty(replacement = "app.acme.name", reason = "not a userful property")
@Deprecated
public String getDeprecated() {
return deprecated;
}
@Deprecated
public void setDeprecated(String deprecated) {
this.deprecated = deprecated;
}
}
public enum Type{
CREATE,UPDATE,DEL,ADD
}
}
- 重新構建項目會生成target/classes/META-INF/spring-configuration-metadata.json文件
{
"groups": [
{
"name": "spring.yaomy",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties"
},
{
"name": "spring.yaomy.http-client",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceMethod": "getHttpClient()"
},
{
"name": "spring.yaomy.test",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceMethod": "getTest()"
},
{
"name": "spring.yaomy.test.school",
"type": "com.yaomy.control.common.control.conf.School",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"sourceMethod": "getSchool()"
}
],
"properties": [
{
"name": "spring.yaomy.http-client.connect-time-out",
"type": "java.lang.Integer",
"description": "HttpClientService connect timeout (in milliseconds),default:10000",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"defaultValue": 10000
},
{
"name": "spring.yaomy.http-client.read-time-out",
"type": "java.lang.Integer",
"description": "HttpClientService read timeout (in milliseconds),default:5000",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"defaultValue": 5000
},
{
"name": "spring.yaomy.test.context",
"type": "java.util.Map<java.lang.String,java.lang.String>",
"description": "context",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.school.address",
"type": "java.lang.String",
"description": "address",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.school.name",
"type": "java.lang.String",
"description": "name",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.school.no",
"type": "java.lang.Integer",
"description": "學號",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.test-enable",
"type": "java.lang.Boolean",
"description": "enable or disable default false",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"defaultValue": false
},
{
"name": "spring.yaomy.test.test-tip",
"type": "java.lang.String",
"description": "test tip",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.test-type",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$Type",
"description": "test type",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.deprecated",
"type": "java.lang.String",
"description": "deprecated test",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"deprecated": true,
"deprecation": {
"reason": "not a userful property",
"replacement": "app.acme.name"
}
}
],
"hints": []
}
- 上一步我們生成了spring-configuration-metadata.json文件,但是文件中的hints提示屬性爲空,這就需要我們在META-INF目錄下自定義一個additional-spring-configuration-metadata.json文件
{"hints": [
{
"name": "spring.yaomy.test.context.keys",
"values": [
{
"value": "key1",
"description": "key description1."
},
{
"value": "key2",
"description": "key description2."
}
]
},
{
"name": "spring.yaomy.test.context.values",
"values": [
{
"value": "value1",
"description": "value description1."
},
{
"value": "value2",
"description": "value description2."
}
]
},
{
"name": "spring.yaomy.test.test-tip",
"values": [
{
"value": "add",
"description": "add description1."
},
{
"value": "del",
"description": "del description2."
},
{
"value": "update",
"description": "del description2."
},
{
"value": "query",
"description": "del description2."
}
]
}
]}
- 重新編譯項目生成如下文件
{
"groups": [
{
"name": "spring.yaomy",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties"
},
{
"name": "spring.yaomy.http-client",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceMethod": "getHttpClient()"
},
{
"name": "spring.yaomy.test",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties",
"sourceMethod": "getTest()"
},
{
"name": "spring.yaomy.test.school",
"type": "com.yaomy.control.common.control.conf.School",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"sourceMethod": "getSchool()"
}
],
"properties": [
{
"name": "spring.yaomy.http-client.connect-time-out",
"type": "java.lang.Integer",
"description": "HttpClientService connect timeout (in milliseconds),default:10000",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"defaultValue": 10000
},
{
"name": "spring.yaomy.http-client.read-time-out",
"type": "java.lang.Integer",
"description": "HttpClientService read timeout (in milliseconds),default:5000",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$HttpClient",
"defaultValue": 5000
},
{
"name": "spring.yaomy.test.context",
"type": "java.util.Map<java.lang.String,java.lang.String>",
"description": "context",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.school.address",
"type": "java.lang.String",
"description": "address",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.school.name",
"type": "java.lang.String",
"description": "name",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.school.no",
"type": "java.lang.Integer",
"description": "學號",
"sourceType": "com.yaomy.control.common.control.conf.School"
},
{
"name": "spring.yaomy.test.test-enable",
"type": "java.lang.Boolean",
"description": "enable or disable default false",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"defaultValue": false
},
{
"name": "spring.yaomy.test.test-tip",
"type": "java.lang.String",
"description": "test tip",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.test-type",
"type": "com.yaomy.control.common.control.conf.MetaDataProperties$Type",
"description": "test type",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test"
},
{
"name": "spring.yaomy.test.deprecated",
"type": "java.lang.String",
"description": "deprecated test",
"sourceType": "com.yaomy.control.common.control.conf.MetaDataProperties$Test",
"deprecated": true,
"deprecation": {
"reason": "not a userful property",
"replacement": "app.acme.name"
}
}
],
"hints": [
{
"name": "spring.yaomy.test.context.keys",
"values": [
{
"value": "key1",
"description": "key description1."
},
{
"value": "key2",
"description": "key description2."
}
]
},
{
"name": "spring.yaomy.test.context.values",
"values": [
{
"value": "value1",
"description": "value description1."
},
{
"value": "value2",
"description": "value description2."
}
]
},
{
"name": "spring.yaomy.test.test-tip",
"values": [
{
"value": "add",
"description": "add description1."
},
{
"value": "del",
"description": "del description2."
},
{
"value": "update",
"description": "del description2."
},
{
"value": "query",
"description": "del description2."
}
]
}
]
}
這樣在application.properties配置文件中編寫配置屬性時不管是屬性key還是value都會有對應的提示功能
4.元數據文件生成好後,如何在項目中引入使用呢?這裏有兩種方案
- 直接使用的類上加上@EnableConfigurationProperties(MetaDataProperties.class)
@EnableConfigurationProperties(MetaDataProperties.class)
@Configuration
public class NetWorkConfig {
/**
* 讀取配置屬性服務類
*/
@Autowired
private MetaDataProperties metaDataProperties;
...
/**
* 定義HTTP請求工廠方法
*/
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
//讀取超時5秒,默認無限限制,單位:毫秒
factory.setReadTimeout(metaDataProperties.getHttpClient().getReadTimeOut());
//連接超時10秒,默認無限制,單位:毫秒
factory.setConnectTimeout(metaDataProperties.getHttpClient().getConnectTimeOut());
return factory;
}
}
- 第二種方案是在META-INF/spring.factories文件中加上如下配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yaomy.control.common.control.conf.MetaDataProperties
在具體的類中的使用方法如下:
@Configuration
public class NetWorkConfig {
/**
* 讀取配置屬性服務類
*/
@Autowired
private MetaDataProperties metaDataProperties;
...
/**
* 定義HTTP請求工廠方法
*/
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
//讀取超時5秒,默認無限限制,單位:毫秒
factory.setReadTimeout(metaDataProperties.getHttpClient().getReadTimeOut());
//連接超時10秒,默認無限制,單位:毫秒
factory.setConnectTimeout(metaDataProperties.getHttpClient().getConnectTimeOut());
return factory;
}
}
綜上兩種方法,建議選擇第二種方法,第一種需要在每個用到類中加上一個註解,比較麻煩;第二種方法只需要配置在spring.factories文件中,容器啓動的時候自動的將java bean加載進入容器中,跟使用普通的Java Bean一樣。
GitHub地址:https://github.com/mingyang66/spring-parent/tree/master/sgrain-spring-boot-common-service/doc