Spring Boot2.1.6學習筆記:元數據文件(properties|yml文件自動提示)

參考: 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章