配置元数据
1. 元数据格式
配置元数据文件位于 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” 都是用户使用给定值指定的配置项。
例如server.port
和server.address
可以在application.properties
/application.yaml
如下:
server.port=9090
server.address=127.0.0.1
server:
port: 9090
address: 127.0.0.1
“组”是更高级别的项目,它们本身不指定值,而是为属性提供上下文分组。
例如,server.port
和server.address
properties 是server
群。
不要求每个 “property” 都有一个 “group”。 某些属性可能本身就存在。 |
最后,“提示”是用于帮助用户配置给定属性的附加信息。
例如,当开发人员配置spring.jpa.hibernate.ddl-auto
属性中,工具可以使用提示为none
,validate
,update
,create
和create-drop
值。
1.1. 组属性
包含在groups
数组可以包含下表中显示的属性:
名字 | 类型 | 目的 |
---|---|---|
|
字符串 |
组的全名。 此属性是必需的。 |
|
字符串 |
组数据类型的类名。
例如,如果组基于一个带有 |
|
字符串 |
可向用户显示的组的简短描述。
如果没有可用的描述,则可以省略它。
建议描述为简短的段落,第一行提供简明的摘要。
描述中的最后一行应以句点 ( |
|
字符串 |
提供此组的源的类名。
例如,如果组基于 |
|
字符串 |
提供此组的方法的全名(包括括号和参数类型)(例如,一个 |
1.2. 属性属性
包含在properties
数组可以包含下表中描述的属性:
名字 | 类型 | 目的 |
---|---|---|
|
字符串 |
属性的全名。
名称采用小写句点分隔形式(例如 |
|
字符串 |
属性数据类型的完整签名(例如 |
|
字符串 |
可向用户显示的属性的简短说明。
如果没有可用的描述,则可以省略它。
建议描述为简短的段落,第一行提供简明的摘要。
描述中的最后一行应以句点 ( |
|
字符串 |
提供此属性的源的类名。
例如,如果属性来自一个带有 |
|
对象 |
默认值,如果未指定属性,则使用该值。 如果属性的类型是数组,它可以是值的数组。 如果默认值未知,则可以省略它。 |
|
折旧 |
指定是否弃用该属性。
如果该字段未弃用或该信息未知,则可以省略该字段。
下表提供了有关 |
包含在deprecation
每个properties
元素可以包含以下属性:
名字 | 类型 | 目的 |
---|---|---|
|
字符串 |
弃用级别,可以是 |
|
字符串 |
弃用属性的原因的简短描述。
如果没有可用的原因,则可以省略。
建议描述为简短的段落,第一行提供简明的摘要。
描述中的最后一行应以句点 ( |
|
字符串 |
替换此已弃用属性的属性的全名。 如果此属性没有替代项,则可以省略它。 |
在 Spring Boot 1.3 之前,单个deprecated boolean 属性代替deprecation 元素。
这仍然以已弃用的方式受支持,不应再使用。
如果没有可用的原因和替换项,则为空的deprecation object 的 |
弃用也可以在代码中以声明方式指定,方法是将@DeprecatedConfigurationProperty
注解传递给 getter 来公开已弃用的属性。
例如,假设my.app.target
property 令人困惑,并被重命名为my.app.name
.
以下示例显示如何处理这种情况:
@ConfigurationProperties("my.app")
public class MyProperties {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Deprecated
@DeprecatedConfigurationProperty(replacement = "my.app.name")
public String getTarget() {
return this.name;
}
@Deprecated
public void setTarget(String target) {
this.name = target;
}
}
There is no way to set a level
.
warning
is always assumed, since code is still handling the property.
The preceding code makes sure that the deprecated property still works (delegating to the name
property behind the scenes).
Once the getTarget
and setTarget
methods can be removed from your public API, the automatic deprecation hint in the metadata goes away as well.
If you want to keep a hint, adding manual metadata with an error
deprecation level ensures that users are still informed about that property.
Doing so is particularly useful when a replacement
is provided.
1.3. Hint Attributes
The JSON object contained in the hints
array can contain the attributes shown in the following table:
Name
Type
Purpose
name
String
The full name of the property to which this hint refers.
Names are in lower-case period-separated form (such as spring.mvc.servlet.path
).
If the property refers to a map (such as system.contexts
), the hint either applies to the keys of the map (system.contexts.keys
) or the values (system.contexts.values
) of the map.
This attribute is mandatory.
values
ValueHint[]
A list of valid values as defined by the ValueHint
object (described in the next table).
Each entry defines the value and may have a description.
providers
ValueProvider[]
A list of providers as defined by the ValueProvider
object (described later in this document).
Each entry defines the name of the provider and its parameters, if any.
The JSON object contained in the values
attribute of each hint
element can contain the attributes described in the following table:
Name
Type
Purpose
value
Object
A valid value for the element to which the hint refers.
If the type of the property is an array, it can also be an array of value(s).
This attribute is mandatory.
description
String
A short description of the value that can be displayed to users.
If no description is available, it may be omitted.
It is recommended that descriptions be short paragraphs, with the first line providing a concise summary.
The last line in the description should end with a period (.
).
The JSON object contained in the providers
attribute of each hint
element can contain the attributes described in the following table:
Name
Type
Purpose
name
String
The name of the provider to use to offer additional content assistance for the element to which the hint refers.
parameters
JSON object
Any additional parameter that the provider supports (check the documentation of the provider for more details).
1.4. Repeated Metadata Items
Objects with the same “property” and “group” name can appear multiple times within a metadata file.
For example, you could bind two separate classes to the same prefix, with each having potentially overlapping property names.
While the same names appearing in the metadata multiple times should not be common, consumers of metadata should take care to ensure that they support it.
2. Providing Manual Hints
To improve the user experience and further assist the user in configuring a given property, you can provide additional metadata that:
-
Describes the list of potential values for a property.
-
Associates a provider, to attach a well defined semantic to a property, so that a tool can discover the list of potential values based on the project’s context.
2.1. Value Hint
The name
attribute of each hint refers to the name
of a property.
In the initial example shown earlier, we provide five values for the spring.jpa.hibernate.ddl-auto
property: none
, validate
, update
, create
, and create-drop
.
Each value may have a description as well.
If your property is of type Map
, you can provide hints for both the keys and the values (but not for the map itself).
The special .keys
and .values
suffixes must refer to the keys and the values, respectively.
Assume a my.contexts
maps magic String
values to an integer, as shown in the following example:
@ConfigurationProperties("my")
public class MyProperties {
private Map<String, Integer> contexts;
}
The magic values are (in this example) are sample1
and sample2
.
In order to offer additional content assistance for the keys, you could add the following JSON to the manual metadata of the module:
{"hints": [
{
"name": "my.contexts.keys",
"values": [
{
"value": "sample1"
},
{
"value": "sample2"
}
]
}
]}
We recommend that you use an Enum
for those two values instead.
If your IDE supports it, this is by far the most effective approach to auto-completion.
2.2. Value Providers
Providers are a powerful way to attach semantics to a property.
In this section, we define the official providers that you can use for your own hints.
However, your favorite IDE may implement some of these or none of them.
Also, it could eventually provide its own.
As this is a new feature, IDE vendors must catch up with how it works.
Adoption times naturally vary.
The following table summarizes the list of supported providers:
Name
Description
any
Permits any additional value to be provided.
class-reference
Auto-completes the classes available in the project.
Usually constrained by a base class that is specified by the target
parameter.
handle-as
Handles the property as if it were defined by the type defined by the mandatory target
parameter.
logger-name
Auto-completes valid logger names and logger groups.
Typically, package and class names available in the current project can be auto-completed as well as defined groups.
spring-bean-reference
Auto-completes the available bean names in the current project.
Usually constrained by a base class that is specified by the target
parameter.
spring-profile-name
Auto-completes the available Spring profile names in the project.
Only one provider can be active for a given property, but you can specify several providers if they can all manage the property in some way.
Make sure to place the most powerful provider first, as the IDE must use the first one in the JSON section that it can handle.
If no provider for a given property is supported, no special content assistance is provided, either.
2.2.1. Any
The special any provider value permits any additional values to be provided.
Regular value validation based on the property type should be applied if this is supported.
This provider is typically used if you have a list of values and any extra values should still be considered as valid.
The following example offers on
and off
as auto-completion values for system.state
:
{"hints": [
{
"name": "system.state",
"values": [
{
"value": "on"
},
{
"value": "off"
}
],
"providers": [
{
"name": "any"
}
]
}
]}
Note that, in the preceding example, any other value is also allowed.
2.2.2. Class Reference
The class-reference provider auto-completes classes available in the project.
This provider supports the following parameters:
Parameter
Type
Default value
Description
target
String
(Class
)
none
The fully qualified name of the class that should be assignable to the chosen value.
Typically used to filter out-non candidate classes.
Note that this information can be provided by the type itself by exposing a class with the appropriate upper bound.
concrete
boolean
true
Specify whether only concrete classes are to be considered as valid candidates.
The following metadata snippet corresponds to the standard server.servlet.jsp.class-name
property that defines the JspServlet
class name to use:
{"hints": [
{
"name": "server.servlet.jsp.class-name",
"providers": [
{
"name": "class-reference",
"parameters": {
"target": "jakarta.servlet.http.HttpServlet"
}
}
]
}
]}
2.2.3. Handle As
The handle-as provider lets you substitute the type of the property to a more high-level type.
This typically happens when the property has a java.lang.String
type, because you do not want your configuration classes to rely on classes that may not be on the classpath.
This provider supports the following parameters:
Parameter
Type
Default value
Description
target
String
(Class
)
none
The fully qualified name of the type to consider for the property.
This parameter is mandatory.
The following types can be used:
-
Any java.lang.Enum
: Lists the possible values for the property.
(We recommend defining the property with the Enum
type, as no further hint should be required for the IDE to auto-complete the values)
-
java.nio.charset.Charset
: Supports auto-completion of charset/encoding values (such as UTF-8
)
-
java.util.Locale
: auto-completion of locales (such as en_US
)
-
org.springframework.util.MimeType
: Supports auto-completion of content type values (such as text/plain
)
-
org.springframework.core.io.Resource
: Supports auto-completion of Spring’s Resource abstraction to refer to a file on the filesystem or on the classpath (such as classpath:/sample.properties
)
If multiple values can be provided, use a Collection
or Array type to teach the IDE about it.
The following metadata snippet corresponds to the standard spring.liquibase.change-log
property that defines the path to the changelog to use.
It is actually used internally as a org.springframework.core.io.Resource
but cannot be exposed as such, because we need to keep the original String value to pass it to the Liquibase API.
{"hints": [
{
"name": "spring.liquibase.change-log",
"providers": [
{
"name": "handle-as",
"parameters": {
"target": "org.springframework.core.io.Resource"
}
}
]
}
]}
2.2.4. Logger Name
The logger-name provider auto-completes valid logger names and logger groups.
Typically, package and class names available in the current project can be auto-completed.
If groups are enabled (default) and if a custom logger group is identified in the configuration, auto-completion for it should be provided.
Specific frameworks may have extra magic logger names that can be supported as well.
This provider supports the following parameters:
Parameter
Type
Default value
Description
group
boolean
true
Specify whether known groups should be considered.
Since a logger name can be any arbitrary name, this provider should allow any value but could highlight valid package and class names that are not available in the project’s classpath.
The following metadata snippet corresponds to the standard logging.level
property.
Keys are logger names, and values correspond to the standard log levels or any custom level.
As Spring Boot defines a few logger groups out-of-the-box, dedicated value hints have been added for those.
{"hints": [
{
"name": "logging.level.keys",
"values": [
{
"value": "root",
"description": "Root logger used to assign the default logging level."
},
{
"value": "sql",
"description": "SQL logging group including Hibernate SQL logger."
},
{
"value": "web",
"description": "Web logging group including codecs."
}
],
"providers": [
{
"name": "logger-name"
}
]
},
{
"name": "logging.level.values",
"values": [
{
"value": "trace"
},
{
"value": "debug"
},
{
"value": "info"
},
{
"value": "warn"
},
{
"value": "error"
},
{
"value": "fatal"
},
{
"value": "off"
}
],
"providers": [
{
"name": "any"
}
]
}
]}
2.2.5. Spring Bean Reference
The spring-bean-reference provider auto-completes the beans that are defined in the configuration of the current project.
This provider supports the following parameters:
Parameter
Type
Default value
Description
target
String
(Class
)
none
The fully qualified name of the bean class that should be assignable to the candidate.
Typically used to filter out non-candidate beans.
The following metadata snippet corresponds to the standard spring.jmx.server
property that defines the name of the MBeanServer
bean to use:
{"hints": [
{
"name": "spring.jmx.server",
"providers": [
{
"name": "spring-bean-reference",
"parameters": {
"target": "javax.management.MBeanServer"
}
}
]
}
]}
The binder is not aware of the metadata.
If you provide that hint, you still need to transform the bean name into an actual Bean reference using by the ApplicationContext
.
2.2.6. Spring Profile Name
The spring-profile-name provider auto-completes the Spring profiles that are defined in the configuration of the current project.
The following metadata snippet corresponds to the standard spring.profiles.active
property that defines the name of the Spring profile(s) to enable:
{"hints": [
{
"name": "spring.profiles.active",
"providers": [
{
"name": "spring-profile-name"
}
]
}
]}
3. Generating Your Own Metadata by Using the Annotation Processor
You can easily generate your own configuration metadata file from items annotated with @ConfigurationProperties
by using the spring-boot-configuration-processor
jar.
The jar includes a Java annotation processor which is invoked as your project is compiled.
3.1. Configuring the Annotation Processor
To use the processor, include a dependency on spring-boot-configuration-processor
.
With Maven the dependency should be declared as optional, as shown in the following example:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
With Gradle, the dependency should be declared in the annotationProcessor
configuration, as shown in the following example:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}
If you are using an additional-spring-configuration-metadata.json
file, the compileJava
task should be configured to depend on the processResources
task, as shown in the following example:
tasks.named('compileJava') {
inputs.files(tasks.named('processResources'))
}
This dependency ensures that the additional metadata is available when the annotation processor runs during compilation.
If you are using AspectJ in your project, you need to make sure that the annotation processor runs only once.
There are several ways to do this.
With Maven, you can configure the maven-apt-plugin
explicitly and add the dependency to the annotation processor only there.
You could also let the AspectJ plugin run all the processing and disable annotation processing in the maven-compiler-plugin
configuration, as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<proc>none</proc>
</configuration>
</plugin>
If you are using Lombok in your project, you need to make sure that its annotation processor runs before spring-boot-configuration-processor
.
To do so with Maven, you can list the annotation processors in the right order using the annotationProcessors
attribute of the Maven compiler plugin.
If you are not using this attribute, and annotation processors are picked up by the dependencies available on the classpath, make sure that the lombok
dependency is defined before the spring-boot-configuration-processor
dependency.
3.2. Automatic Metadata Generation
The processor picks up both classes and methods that are annotated with @ConfigurationProperties
.
If the class has a single parameterized constructor, one property is created per constructor parameter, unless the constructor is annotated with @Autowired
.
If the class has a constructor explicitly annotated with @ConstructorBinding
, one property is created per constructor parameter for that constructor.
Otherwise, properties are discovered through the presence of standard getters and setters with special handling for collection and map types (that is detected even if only a getter is present).
The annotation processor also supports the use of the @Data
, @Value
, @Getter
, and @Setter
lombok annotations.
Consider the following example:
@ConfigurationProperties(prefix = "my.server")
public class MyServerProperties {
/**
* Name of the server.
*/
private String name;
/**
* IP address to listen to.
*/
private String ip = "127.0.0.1";
/**
* Port to listener to.
*/
private int port = 9797;
This exposes three properties where my.server.name
has no default and my.server.ip
and my.server.port
defaults to "127.0.0.1"
and 9797
respectively.
The Javadoc on fields is used to populate the description
attribute. For instance, the description of my.server.ip
is "IP address to listen to.".
You should only use plain text with @ConfigurationProperties
field Javadoc, since they are not processed before being added to the JSON.
The annotation processor applies a number of heuristics to extract the default value from the source model.
Default values have to be provided statically. In particular, do not refer to a constant defined in another class.
Also, the annotation processor cannot auto-detect default values for Enum
s and Collections
s.
For cases where the default value could not be detected, manual metadata should be provided.
Consider the following example:
@ConfigurationProperties(prefix = "my.messaging")
public class MyMessagingProperties {
private List<String> addresses = new ArrayList<>(Arrays.asList("a", "b"));
private ContainerType containerType = ContainerType.SIMPLE;
public enum ContainerType {
SIMPLE, DIRECT
}
}
In order to document default values for properties in the class above, you could add the following content to the manual metadata of the module:
{"properties": [
{
"name": "my.messaging.addresses",
"defaultValue": ["a", "b"]
},
{
"name": "my.messaging.container-type",
"defaultValue": "simple"
}
]}
Only the name
of the property is required to document additional metadata for existing properties.
3.2.1. Nested Properties
The annotation processor automatically considers inner classes as nested properties.
Rather than documenting the ip
and port
at the root of the namespace, we could create a sub-namespace for it.
Consider the updated example:
@ConfigurationProperties(prefix = "my.server")
public class MyServerProperties {
private String name;
private Host host;
public static class Host {
private String ip;
private int port;
}
}
The preceding example produces metadata information for my.server.name
, my.server.host.ip
, and my.server.host.port
properties.
You can use the @NestedConfigurationProperty
annotation on a field to indicate that a regular (non-inner) class should be treated as if it were nested.
This has no effect on collections and maps, as those types are automatically identified, and a single metadata property is generated for each of them.
3.3. Adding Additional Metadata
Spring Boot’s configuration file handling is quite flexible, and it is often the case that properties may exist that are not bound to a @ConfigurationProperties
bean.
You may also need to tune some attributes of an existing key.
To support such cases and let you provide custom "hints", the annotation processor automatically merges items from META-INF/additional-spring-configuration-metadata.json
into the main metadata file.
If you refer to a property that has been detected automatically, the description, default value, and deprecation information are overridden, if specified.
If the manual property declaration is not identified in the current module, it is added as a new property.
The format of the additional-spring-configuration-metadata.json
file is exactly the same as the regular spring-configuration-metadata.json
.
The additional properties file is optional.
If you do not have any additional properties, do not add the file.