• Spring Boot「09」Property 高级特性


    在Spring Boot「08」设置和使用 Property中,我们学习了 Spring / Spring Boot 时如何处理外部配置文件及如何在应用中使用配置文件中的 Property。今天,我们将进一步学习 Spring Boot 中与 Property 使用相关的高级特性。

    01-@ConfigurationProperties注解

    在定义 Property 时,一个常用的做法是通过公共前缀对某一类相关地 Property 进行区分,例如下面的配置:

    example.database.mysql.url=jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=trueexample.database.mysql.user=userexample.database.mysql.password=password
    example.database.redis.host=localhostexample.database.redis.port=6379example.database.redis.database=1

    这种定义 Property 的方式,在 Spring 中被称为层次化属性(hierarchical properties)。Spring Boot 中定义了一个注解@ConfigurationProperties + @Configuration来帮助开发者方便地将层次化的属性绑定到多个 POJO 中,例如:

    @Configuration@ConfigurationProperties(prefix = "example.database.mysql")public class MysqlConfigProperties {    private String url;    private String user;    private String password;}@Configuration@ConfigurationProperties(prefix = "example.database.redis")public class RedisConfigProperties {    private String host;    private Integer port;    private Integer database;}
    

    复制代码

    除了上述这种方式,还可以通过在 *Application 类上标注@EnableConfigurationProperties,并通过其 value 属性来指定@ConfigurationProperties标注的 POJO 类,例如:

    /** 等价于上述 @ConfigurationProperties + @Configuration 的方式 */@ConfigurationProperties(prefix = "example.database.mysql")public class MysqlConfigProperties { /** ... */ }
    @ConfigurationProperties(prefix = "example.database.redis")public class RedisConfigProperties { /** ... */ }
    @SpringBootApplication@EnableConfigurationProperties({MysqlConfigProperties.class, RedisConfigProperties.class})public class PropertiesApplication { /** ... */ }
    

    复制代码

    另外还有一种方式,使用 Spring Boot 2.2 及以上版本的应用中,还可通过@ConfigurationPropertiesScan来自动扫描特定包下标有@ConfigurationProperties注解的类。若不指定包名,则默认扫描@ConfigurationPropertiesScan标注的类所在包下所有的标注@ConfigurationProperties的类或@Bean方法。

    注:通过ConfigurationProperties向 POJO 对象中注入值时以来 POJO 类中的 setters 因此,若无对应 setter,Spring Boot 是无法将属性注入到 Bean 中的。

    除了标注在 class 上,该注解还可以标注在@Bean方法上,例如:

    @ConfigurationProperties(prefix = "external")@Beanpublic ExternalProperties external() {    return new ExternalProperties();}
    

    复制代码

    当标注在@Bean方法上时,如果对应类没有 setter 方法,则会抛 ConfigurationPropertiesBindException 异常。可以通过@ConfigurationProperties(ignoreInvalidFields = true)来跳过此类错误。

    @ConfigurationProperties支持 Property 嵌套,包括 List / Map / Class,例如:

    nested.mail.addresses[0]=samson@mail.comnested.mail.addresses[1]=foo@mail.comnested.mail.addresses[2]=bar@mail.com
    nested.mail.contacts.firstname=samsonnested.mail.contacts.lastname=bu
    nested.mail.external.foo=barnested.mail.external.bar=foo
    

    复制代码

    @ConfigurationProperties(prefix = "nested.mail")public class NestedProperties {    private List<String> addresses;    private Map<String, String> contacts;    private ExternalProperties external;}
    

    复制代码

    02-@Value注解

    @Value是 spring-beans 中提供的一个注解,用于向托管在 Spring 容器中的 Bean 的属性注入值。该注解可以标注在属性、类构造器或类方法上。该注解仅包含一个 value 属性,value 的值可以是:

    • plain string,例如 "Hello, world!":

    @Componentclass Demo {    @Value("Hello, world!")    private String str;}
    

    复制代码

    • property placeholder,例如 "${example.str}":

    @Componentclass Demo {    @Value("${example.str}")    private String str;}
    

    复制代码

    注:这种写法有个问题,如果 Property example.str 在 Environment 中找不到,则会抛 BeanCreationException 异常,此时可通过下述默认值的方式,在 Property 不存在时,将默认值赋予类属性

    • property placeholder with default value,例如 "${example.str:Hello, world!}":

    @Componentclass Demo {    @Value("${example.str:Hello, world!}")    private String str;}
    

    复制代码

    注:如果@Value的目标属性为数组时,Spring Boot 默认以","作为分隔符,例如:

    1. @Value("${external.foo:Hello, world!}")
    2. @Delimiter(value = "#")
    3. private String[] externalFooWithDefaults;

    当不使用@Delimiter指定分隔符时,externalFooWithDefaults = {"Hello", "world!"},指定分隔符为"#"后,值为 {"Hello, world!"}

    • SpEL expression。这里不再细究,可以参考官网介绍1

    @Value除了能够标注在类属性上,还可以标注在构造器、Setter 方法上:

    /** 注解在构造器上 */private String foo;public ValueAnnotationDemo(@Value("${external.bar}") String foo) {    this.foo = foo;}/** 注解在 setter 上 */public void setFoo(@Value("${external.foo}") String foo) {    this.foo = foo;}
    

    复制代码

    我们知道 Spring Context 是支持分层的,即 Context 之间可以具有父子关系。在 parent-context 定义的属性,在 parent-context 和 child-context 中都可以访问,即@Value和Environment#getProperty()是可以去到值的;在 child-context 定义的属性,在 child-context 中@Value和Environment#getProperty()是可以访问的;在 parent-context 中两种方式都不可访问;

  • 相关阅读:
    剖析flutter_download_manager学习如何做下载管理,暂停和取消
    腾讯二面vue面试题总结
    年产3000吨冲压型果味硬糖生产车间工艺设计
    kubernetes介绍及安装(一)
    julia 笔记:向量化 . 运算符(广播)
    P1147 连续自然数和 【双指针(尺取法)】
    【故障公告】龙卷风继续袭击,服务器们顽强抵抗
    Springboot 集成 mongodb
    linux文件输入输出的重定向
    03【C语言 & 趣味算法】(值得品味的一道题)打鱼还是晒网?结构体的简单应用。函数的应用。判断闰年的应用。求指定日期距1990年1月1日的天数。
  • 原文地址:https://blog.csdn.net/Q54665642ljf/article/details/127420951