微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

20220602 Appendix - 2. Configuration Metadata

前言

文档地址

Spring Boot jar 包含元数据文件,这些文件提供了所有受支持的配置属性的详细信息。这些文件旨在让 IDE 开发人员在用户使用 application.propertiesapplication.yml 文件时提供上下文帮助和 “代码完成” 。

大多数元数据文件是在编译时通过处理所有带有 @ConfigurationProperties 注解的项目自动生成的。但是,可以为极端情况或更高级的用例 手动编写部分元数据

1. 元数据格式

配置元数据文件位于 jar 下的 meta-inf/spring-configuration-Metadata.json 。它们使用 JSON 格式,项目归类为 groupsproperties ,额外的值提示归类为 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.portserver.address 可能在您的 application.properties / application.yaml 中指定,如下所示:

server.port=9090
server.address=127.0.0.1

groups 是更高级别的项目,它们本身不指定值,而是为属性提供上下文分组。例如,server.portserver.address 属性server 组的一部分。

并不要求每个 property 都有一个 groups 。某些属性可能本身就存在。

最后,hints 是用于帮助用户配置给定属性的附加信息。例如,当开发人员配置 spring.jpa.hibernate.ddl-auto 属性时,工具可以使用提示nonevalidateupdatecreatecreate-drop 值提供一些自动完成帮助。

1.1. Group Attributes

groups 数组中包含的 JSON 对象可以包含下表所示的属性

名称 类型 目的
name String 组的全名。该属性是强制性的。
type String 组的数据类型的类名。例如,如果组基于用 @ConfigurationProperties 注解的类,则该属性将包含该类的完全限定名称。如果它基于一个 @Bean 方法,它将是该方法的返回类型。如果类型未知,则可以省略该属性
description String 可以向用户显示的组的简短描述。如果没有可用的描述,则可以省略。建议描述是简短的段落,第一行提供简洁的摘要。说明中的最后一行应以句点 . 结尾。
sourceType String 贡献此组的源的类名。例如,如果组基于用 @ConfigurationProperties 注解的 @Bean 方法,则此属性将包含包含该方法@Configuration 类的完全限定名称。如果源类型未知,则可以省略该属性
sourceMethod String 贡献此组的方法的全名(包括括号和参数类型)(例如,带 @ConfigurationProperties 注解的 @Bean 方法名称)。如果源方法未知,则可以省略。

1.2. Property Attributes

properties 数组中包含的 JSON 对象可以包含下表中描述的属性

名称 类型 目的
name String 属性的全名。名称采用以句点分隔的小写形式(例如 server.address )。该属性是强制性的。
type String 属性数据类型的完整签名(例如 java.lang.String ),但也是完整的泛型类型(例如 java.util.Map<java.lang.String,com.example.MyEnum> )。您可以使用此属性来指导用户了解他们可以输入的值的类型。为了保持一致性,原语的类型是通过使用其包装对应物来指定的(例如,boolean 变成 java.lang.Boolean )。请注意,此类可能是一个复杂类型,它在绑定值时从 String 转换而来。如果类型未知,则可以省略。
description String 可以向用户显示属性的简短描述。如果没有可用的描述,则可以省略。建议描述是简短的段落,第一行提供简洁的摘要。说明中的最后一行应以句点 ( . ) 结尾。
sourceType String 贡献此属性的源的类名。例如,如果属性来自用 @ConfigurationProperties 注解的类,则该属性将包含该类的完全限定名称。如果源类型未知,则可以省略。
defaultValue Object 认值,在未指定属性时使用。如果属性的类型是一个数组,它可以是一个值数组。如果认值未知,则可以省略。
deprecation Deprecation 指定该属性是否已弃用。如果该字段未被弃用或该信息未知,则可以省略。下表提供了有关 deprecation 属性的更多详细信息。

每个 properties 标签deprecation 属性中包含的 JSON 对象可以包含以下属性

名称 类型 目的
level String 弃用级别,可以是 warning认值)或 error 。当属性具有 warning 弃用级别时,它仍应绑定在环境中。但是,当它具有 error 弃用级别时,该属性将不再受管理且不受约束。
reason String 属性被弃用的原因的简短描述。如果没有理由,可以省略。建议描述是简短的段落,第一行提供简洁的摘要。说明中的最后一行应以句点 ( . ) 结尾。
replacement String 替换 此弃用属性属性的全名。如果此属性没有替代品,则可以省略。

在 Spring Boot 1.3 之前,可以使用单个 deprecated 布尔属性来代替 deprecation 标签。这仍然以不推荐的方式支持,不应再使用。如果没有原因和替换可用,则应设置一个deprecation 对象。

也可以通过将 @DeprecatedConfigurationProperty 注解添加到公开已弃用属性的 getter 来在代码中以声明方式指定弃用。例如,假设 my.app.target 属性令人困惑并被重命名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;
    }

}

没有办法设置 level 。 始终假定 warning ,因为代码仍在处理该属性

前面的代码确保不推荐使用的属性仍然有效(委托给底层的 name 属性)。一旦可以从公共 API 中删除 getTargetsetTarget 方法,元数据中的自动弃用提示也会消失。如果您想保留提示添加具有 error 弃用级别的手动元数据可确保用户仍了解该属性。当提供 replacement 时,这样做特别有用。

1.3. Hint Attributes

hints 数组中包含的 JSON 对象可以包含下表所示的属性

名称 类型 目的
name String 提示所指的属性的全名。名称采用小写句点分隔形式(例如 spring.mvc.servlet.path )。如果属性引用映射(例如 system.contexts ),则提示要么应用于映射的system.contexts.keys ),要么应用于映射的system.contexts.values )。该属性是强制性的。
values ValueHint[] ValueHint 对象定义的有效值列表(在下表中描述)。每个条目定义值并且可能有描述。
providers ValueProvider[] ValueProvider 对象定义的提供者列表(本文档稍后描述)。每个条目定义提供者的名称及其参数(如果有)。

每个 hint 标签values 属性中包含的 JSON 对象可以包含下表中描述的属性

名称 类型 目的
value Object 提示所指元素的有效值。如果属性的类型是一个数组,它也可以是一个值数组。该属性是强制性的。
description String 可以向用户显示的值的简短描述。如果没有可用的描述,则可以省略。建议描述是简短的段落,第一行提供简洁的摘要。说明中的最后一行应以句点 ( . ) 结尾。

每个 hint 标签providers 属性中包含的 JSON 对象可以包含下表中描述的属性

姓名 类型 目的
name String provider 的名称,用于为提示所指的标签提供额外的内容帮助。
parameters JSON 对象 provider 支持的任何附加参数(有关更多详细信息,请查看 provider 的文档)。

1.4. 重复的元数据项

具有相同 propertygroup 名称的对象可以在元数据文件中出现多次。例如,您可以将两个单独的类绑定到相同的前缀,每个类都有可能重叠的属性名称。虽然多次出现在元数据中的相同名称不应该是常见的,但元数据的消费者应该注意确保他们支持它。

2. Providing Manual Hints

为了改善用户体验并进一步帮助用户配置给定的属性,您可以提供额外的元数据:

  • 描述属性的潜在值列表。
  • 关联提供者,将定义明确的语义附加到属性,以便工具可以根据项目的上下文发现潜在值的列表。

2.1. Value Hint

每个提示(hint)的 name 元素指的是一个 name 属性。在前面显示的初始示例中,我们为 spring.jpa.hibernate.ddl-auto 属性提供了五个值:nonevalidateupdatecreatecreate-drop。每个值也可能有描述。

如果您的属性Map 类型,您可以为键和值提供提示(但不是为 map 本身)。特殊的 .keys.values 后缀必须分别引用键和值。

假设 my.contexts 将魔法 String 值映射到整数,如以下示例所示:

@ConfigurationProperties("my")
public class MyProperties {

    private Map<String, Integer> contexts;

    // getters/setters ...

}

魔法值是(在这个例子中)是 sample1sample2 。为了为键提供额外的内容帮助,您可以将以下 JSON 添加模块的手动元数据中

{"hints": [
    {
        "name": "my.contexts.keys",
        "values": [
            {
                "value": "sample1"
            },
            {
                "value": "sample2"
            }
        ]
    }
]}

我们建议您将 Enum 用于这两个值。如果您的 IDE 支持它,这是迄今为止最有效的自动完成方法

2.2. Value Providers

Provider 是一种将语义附加到属性的强大方法。在本节中,我们定义了可用于您自己的提示的官方提供程序。但是,您最喜欢的 IDE 可能会实现其中的一些,也可能不实现。此外,它最终可以提供自己的。

由于这是一项新功能,IDE 供应商必须赶上它的工作原理。采用时间自然会有所不同。

下表总结了支持的 provider 列表:

名称 描述
any 允许提供任何额外值。
class-reference 自动完成项目中可用的类。通常受 target 参数指定的基类约束。
handle-as 处理属性,就好像它是由强制 target 参数定义的类型定义的一样。
logger-name 自动完成有效的记录器名称记录器组 。通常,当前项目中可用的包和类名称可以自动完成以及定义的组。
spring-bean-reference 自动完成当前项目中可用的 bean 名称。通常受 target 参数指定的基类约束。
spring-profile-name 自动完成项目中可用的 Spring 配置文件名称

对于给定的属性,只能有一个 provider 处于活动状态,但如果他们都可以以某种方式管理该属性,则您可以指定多个 provider 。确保首先放置最强大的 provider ,因为 IDE 必须使用它可以处理的 JSON 部分中的第一个。如果不支持给定属性的 provider ,则也不提供特殊的内容帮助。

2.2.1. Any

特殊的 any provider 值允许提供任何附加值。如果支持,则应应用基于属性类型的常规值验证。

如果您有一个值列表并且任何额外的值仍应被视为有效,则通常使用此 provider 。

以下示例提供 onoff 作为 system.state自动提示值:

{"hints": [
    {
        "name": "system.state",
        "values": [
            {
                "value": "on"
            },
            {
                "value": "off"
            }
        ],
        "providers": [
            {
                "name": "any"
            }
        ]
    }
]}

请注意,在前面的示例中,也允许使用任何其他值。

2.2.2. Class Reference

class-reference provider 自动提示项目中可用的类。此 provider 支持以下参数:

范围 类型 认值 描述
target String (Class) none 应该可分配给所选值的类的完全限定名称。通常用于过滤掉非候选类。请注意,此信息可以由类型本身通过公开具有适当上限的类来提供。
concrete boolean true 指定是否仅将具体类视为有效候选者。

以下元数据片段对应于定义要使用的 JspServlet 类名的标准 server.servlet.jsp.class-name 属性

{"hints": [
    {
        "name": "server.servlet.jsp.class-name",
        "providers": [
            {
                "name": "class-reference",
                "parameters": {
                    "target": "javax.servlet.http.HttpServlet"
                }
            }
        ]
    }
]}

2.2.3. Handle As

handle-as provider 允许您将属性的类型替换为更高级的类型。这通常发生在属性具有 java.lang.String 类型时,因为您不希望配置类依赖于可能不在类路径上的类。此提供程序支持以下参数:

范围 类型 认值 描述
target String ( Class ) none 要为属性考虑的类型的完全限定名称。此参数是必需的。

可以使用以下类型:

  • 任何 java.lang.Enum :列出属性的可能值。(我们建议使用类型定义 Enum 属性,因为 IDE 无需进一步提示即可自动提示值)
  • java.nio.charset.Charset支持字符集/编码值的自动补全(如 UTF-8
  • java.util.Locale :语言环境的自动完成(例如 en_US
  • org.springframework.util.MimeType支持内容类型值的自动补全(如 text/plain
  • org.springframework.core.io.Resource支持自动提示 Spring 的 Resource 抽象来引用文件系统或类路径上的文件(例如classpath:/sample.properties

如果可以提供多个值,请使用 CollectionArray 类型向 IDE 介绍它。

以下元数据片段对应于定义要使用的更改日志路径的标准 spring.liquibase.change-log 属性。它实际上在内部用作 org.springframework.core.io.Resource 但不能这样公开,因为我们需要保留原始 String 值以将其传递给 Liquibase API。

{"hints": [
    {
        "name": "spring.liquibase.change-log",
        "providers": [
            {
                "name": "handle-as",
                "parameters": {
                    "target": "org.springframework.core.io.Resource"
                }
            }
        ]
    }
]}

2.2.4. Logger Name

logger-name provider 自动提示有效的记录器名称记录器组 。通常,当前项目中可用的包名和类名可以自动提示。如果启用了组(认)并且如果在配置中标识了自定义记录器组,则应为其提供自动提示功能。特定的框架可能有额外的魔法记录器名称,也可以支持

此提供程序支持以下参数:

范围 类型 认值 描述
group boolean true 指定是否应考虑已知组。

由于记录器名称可以是任意名称,因此此 provider 应允许任何值,但可以突出显示项目类路径中不可用的有效包和类名称

以下元数据片段对应于标准 logging.level 属性。键是记录器名称,值对应于标准日志级别或任何自定义级别。由于 Spring Boot 定义了一些开箱即用的记录器组,因此为它们添加了专用的值提示

{"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

spring-bean-reference provider 自动提示当前项目配置中定义的 bean 。此 provider 支持以下参数:

范围 类型 认值 描述
target String (Class) none 应分配给候选者的 bean 类的完全限定名称。通常用于过滤掉非候选 bean。

以下元数据片段对应于定义要使用的 MBeanServer bean 名称的标准 spring.jmx.server 属性

{"hints": [
    {
        "name": "spring.jmx.server",
        "providers": [
            {
                "name": "spring-bean-reference",
                "parameters": {
                    "target": "javax.management.MBeanServer"
                }
            }
        ]
    }
]}

binder 不知道元数据。如果您提供该提示,您仍然需要将 bean 名称转换为 ApplicationContext 使用的实际 Bean 引用。

2.2.6. Spring Profile Name

spring-profile-name provider 自动提示在当前项目的配置中定义的 Spring profile 。

以下元数据片段对应于标准 spring.profiles.active 属性,该属性定义要启用的 Spring 配置文件名称

{"hints": [
    {
        "name": "spring.profiles.active",
        "providers": [
            {
                "name": "spring-profile-name"
            }
        ]
    }
]}

3. 使用注解处理器(Annotation Processor)生成您自己的元数据

您可以使用 spring-boot-configuration-processor jar从带有 @ConfigurationProperties 注解的项目上轻松生成自己的配置元数据文件。该 jar 包含一个 Java 注解处理器,它在您的项目编译时被调用

3.1. 配置注解处理器

要使用处理器,请包含 spring-boot-configuration-processor 依赖

在 Maven 中,依赖项应声明为可选,如下例所示:

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

使用 Gradle,应在配置中声明 annotationProcessor 依赖项,如以下示例所示:

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}

如果您使用的是 additional-spring-configuration-Metadata.json 文件,则应将 compileJava 任务配置为依赖于 processResources 任务,如下例所示:

tasks.named('compileJava') {
    inputs.files(tasks.named('processResources'))
}

此依赖关系确保在编译期间注解处理器运行时附加元数据可用。

如果您在项目中使用 AspectJ,则需要确保注解处理器只运行一次。有几种方法可以做到这一点。使用 Maven,您可以显式配置并将 maven-apt-plugin 依赖项添加到注解处理器。您还可以让 AspectJ 插件运行所有处理并在 maven-compiler-plugin 配置中禁用注解处理,如下所示:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <proc>none</proc>
    </configuration>
</plugin>

3.2. 自动元数据生成

处理器拾取带有 @ConfigurationProperties 注解的类和方法

如果该类也用 @ConstructorBinding 注解,则需要一个构造函数,并且每个构造函数参数创建一个属性。否则,通过对集合和映射类型进行特殊处理的标准 getter 和 setter 的存在来发现属性(即使仅存在 getter 也会被检测到)。注解处理器还支持使用 @Data@Value@Getter@Setter lombok 注解。

考虑以下示例:

@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;

    // getters/setters ...

这暴露了三个属性,其中 my.server.name 没有认值 ,my.server.ipmy.server.port 认值分别为 "127.0.0.1"9797 。字段上的 Javadoc 用于填充 description 属性。例如,my.server.ip 的描述是“要收听的IP地址”。

您应该只使用带有 @ConfigurationProperties 字段 Javadoc 的纯文本,因为它们在添加到 JSON 之前不会被处理。

注解处理器应用许多启发式方法从源模型中提取认值。必须静态提供认值。特别是,不要引用另一个类中定义的常量。此外,注解处理器无法自动检测 EnumCollections认值。

对于无法检测到认值的情况,应提供 手动元数据 。考虑以下示例:

@ConfigurationProperties(prefix = "my.messaging")
public class MyMessagingProperties {

    private List<String> addresses = new ArrayList<>(Arrays.asList("a", "b"));

    private ContainerType containerType = ContainerType.SIMPLE;

    // getters/setters ...

    public enum ContainerType {

        SIMPLE, DIRECT

    }

}

为了记录上述类中属性认值,您可以将以下内容添加模块的手动元数据中

{"properties": [
    {
        "name": "my.messaging.addresses",
        "defaultValue": ["a", "b"]
    },
    {
        "name": "my.messaging.container-type",
        "defaultValue": "simple"
    }
]}

仅需要属性name 来记录现有属性的其他元数据。

3.2.1. 嵌套属性

注解处理器自动将内部类视为嵌套属性。我们可以为它创建一个子命名空间,而不是在命名空间的根目录下记录 ipport 。考虑更新的示例:

@ConfigurationProperties(prefix = "my.server")
public class MyServerProperties {

    private String name;

    private Host host;

    // getters/setters ...

    public static class Host {

        private String ip;

        private int port;

        // getters/setters ...

    }

}

前面的示例为 my.server.namemy.server.host.ipmy.server.host.port 属性生成元数据信息。您可以在字段上使用 @nestedConfigurationProperty 注解来指示应将常规(非内部)类视为嵌套类。

这对集合和 map 没有影响,因为这些类型是自动识别的,并且会为它们中的每一个生成一个元数据属性

3.3. 添加其他元数据

Spring Boot 的配置文件处理非常灵活,并且经常会出现未绑定到 @ConfigurationProperties bean 的属性。您可能还需要调整现有键的某些属性。为了支持这种情况并让您提供自定义提示”,注解处理器会自动将项目从 meta-inf/additional-spring-configuration-Metadata.json 合并到主元数据文件中。

如果您引用已自动检测到的属性,则描述、认值和弃用信息(如果指定)将被覆盖。如果当前模块中未标识手动属性声明,则将其添加为新属性

additional-spring-configuration-Metadata.json 文件的格式与常规的 spring-configuration-Metadata.json 相同。附加属性文件是可选的。如果您没有任何其他属性,请不要添加文件

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐