SpringBoot中@ConfigurationProperties注解的使用與源碼詳解
前言
相信大家肯定了解@Value注解,它可以通過一個配置文件中的屬性名與對象的屬性進(jìn)行綁定。
@ConfigurationProperties注解的作用其實就類似于使用多個@Value注解同時綁定一個對象的多個屬性,@ConfigurationProperties注解用于自動配置綁定,可以將application.properties配置中的值(準(zhǔn)確來說是Environment中的屬性值)注入到bean對象上,該注解的使用必須先將對象注入到IOC容器中才有配置自動綁定的功能。
@ConfigurationProperties注解的使用
先來看下@ConfigurationProperties的源碼:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Indexed public @interface ConfigurationProperties { // 匹配的前綴 @AliasFor("prefix") String value() default ""; // 同上 @AliasFor("value") String prefix() default ""; // 忽略屬性類型不匹配的字段 boolean ignoreInvalidFields() default false; // 忽略類中未知的屬性,ignoreUnknownFields=false后當(dāng)出現(xiàn)未知字段時會出現(xiàn)異常 boolean ignoreUnknownFields() default true; }
注解使用在類上:
package com.morris.spring.boot.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "database") public class DatabaseProperties { private String username; private String password; private String driverClass; private String connectionUrl; }
配置文件中的屬性名稱需要與實體類的屬性保持一致,不然值會綁定不上,多個單詞可以使用橫杠進(jìn)行分割,SpringBoot會將橫杠命名轉(zhuǎn)駝峰命名。
注解使用方法上:
package com.morris.spring.boot.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; @Configuration public class FlowRuleConfig { @Bean @ConfigurationProperties("flow.config") public RuleProperties ruleProperties() { return new RuleProperties(); } @Data public static class RuleProperties { private Map<String, Integer> rules; } }
@ConfigurationProperties注解的原理
在SpringBoot的spring.factories文件注入了org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration。
ConfigurationPropertiesAutoConfiguration
ConfigurationPropertiesAutoConfiguration上面加了@EnableConfigurationProperties注解。
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties public class ConfigurationPropertiesAutoConfiguration { }
@EnableConfigurationProperties
@EnableConfigurationProperties注解導(dǎo)入了EnableConfigurationPropertiesRegistrar類。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EnableConfigurationPropertiesRegistrar.class) public @interface EnableConfigurationProperties { /** * The bean name of the configuration properties validator. * @since 2.2.0 */ String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator"; /** * Convenient way to quickly register * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with * Spring. Standard Spring Beans will also be scanned regardless of this value. * @return {@code @ConfigurationProperties} annotated beans to register */ Class<?>[] value() default {}; }
EnableConfigurationPropertiesRegistrar
EnableConfigurationPropertiesRegistrar實現(xiàn)了ImportBeanDefinitionRegistrar接口,主要用于向Spring容器中注入Bean。
主要注入了以下Bean:
- ConfigurationPropertiesBindingPostProcessor
- BoundConfigurationProperties
- MethodValidationExcludeFilter
- ConfigurationPropertiesBinder
org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 注入ConfigurationPropertiesBindingPostProcessor // 注入BoundConfigurationProperties registerInfrastructureBeans(registry); // 注入MethodValidationExcludeFilter registerMethodValidationExcludeFilter(registry); // 注入@EnableConfigurationProperties注解指定的ConfigurationProperties // 例如org.springframework.boot.autoconfigure.web.ServerProperties ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry); getTypes(metadata).forEach(beanRegistrar::register); }
ConfigurationPropertiesBindingPostProcessor
ConfigurationPropertiesBindingPostProcessor實現(xiàn)了BeanPostProcessor接口,其postProcessBeforeInitialization()方法會在Bean實例化后執(zhí)行,在這里完成了配置文件的屬性與對象的屬性的綁定。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName)); return bean; } private void bind(ConfigurationPropertiesBean bean) { if (bean == null || hasBoundValueObject(bean.getName())) { return; } Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '" + bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean"); try { // 主要是通過ConfigurationPropertiesBinder來完成配置文件的屬性與對象的屬性的綁定 // 最終會調(diào)用Binder這個類來完成綁定 this.binder.bind(bean); } catch (Exception ex) { throw new ConfigurationPropertiesBindException(bean, ex); } }
ConfigurationPropertiesBinder
ConfigurationPropertiesBinder主要負(fù)責(zé)構(gòu)建Binder并進(jìn)行緩存,SpringBoot啟動過程中已經(jīng)將配置文件的屬性值存到Environment中的PropertySources中了,所以Binder只要從Environment中獲取即可。
ConfigurationPropertiesBinder(ApplicationContext applicationContext) { this.applicationContext = applicationContext; this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources(); this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext); this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext); } BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) { Bindable<?> target = propertiesBean.asBindTarget(); ConfigurationProperties annotation = propertiesBean.getAnnotation(); BindHandler bindHandler = getBindHandler(target, annotation); return getBinder().bind(annotation.prefix(), target, bindHandler); } private Binder getBinder() { if (this.binder == null) { this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(), getConversionServices(), getPropertyEditorInitializer(), null, ConfigurationPropertiesBindConstructorProvider.INSTANCE); } return this.binder; }
如何動態(tài)刷新@ConfigurationProperties
如果配置中心配置更新了,遇到了@ConfigurationProperties標(biāo)注的配置bean,那么bean的屬性就不會自動更新了,那么實現(xiàn)動態(tài)更新@ConfigurationProperties標(biāo)注的bean的屬性呢?
如果使用的Nacos注冊中心,可以監(jiān)聽NacosConfigReceivedEvent事件后使用SpringBoot提供的Binder進(jìn)行bean的屬性的更新:
DatabaseProperties databaseProperties = applicationContext.getBean(DatabaseProperties.class); System.out.println(databaseProperties); // test refresh @ConfigurationProperties // 這里使用app.properties模擬 ClassPathResource classPathResource = new ClassPathResource("app.properties"); ResourcePropertySource resourcePropertySource = new ResourcePropertySource(classPathResource); ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(resourcePropertySource); Binder binder = new Binder(configurationPropertySource); Bindable<DatabaseProperties> bindable = Bindable.ofInstance(databaseProperties); binder.bind("database", bindable); System.out.println(databaseProperties);
當(dāng)前最簡單的辦法就是在bean上面加上@RefreshScope注解就能實現(xiàn)自動刷新屬性值了。
到此這篇關(guān)于SpringBoot中@ConfigurationProperties注解的使用與源碼詳解的文章就介紹到這了,更多相關(guān)@ConfigurationProperties注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中的@ConfigurationProperties注解解析
- 關(guān)于SpringBoot的@ConfigurationProperties注解和松散綁定、數(shù)據(jù)校驗
- SpringBoot2底層注解@ConfigurationProperties配置綁定
- SpringBoot中@ConfigurationProperties注解實現(xiàn)配置綁定的三種方法
- SpringBoot中注解@ConfigurationProperties與@Value的區(qū)別與使用詳解
- SpringBoot @ConfigurationProperties注解的簡單使用
- Springboot之@ConfigurationProperties注解解讀
相關(guān)文章
Java報錯Non-terminating?decimal?expansion解決分析
這篇文章主要為大家介紹了Java報錯Non-terminating?decimal?expansion解決方案及原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Java常用鎖synchronized和ReentrantLock的區(qū)別
這篇文章主要介紹了Java常用鎖synchronized和ReentrantLock的區(qū)別,二者的功效都是相同的,但又有很多不同點,下面我們就進(jìn)入文章了解具體的相關(guān)內(nèi)容吧。需要的小伙伴也可以參考一下2022-05-05Java簡單數(shù)據(jù)加密方法DES實現(xiàn)過程解析
這篇文章主要介紹了Java簡單數(shù)據(jù)加密方法DES實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-12-12Jmeter后置處理器實現(xiàn)過程及方法應(yīng)用
這篇文章主要介紹了Jmeter后置處理器實現(xiàn)過程及方法應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09SpringSecurity實現(xiàn)動態(tài)權(quán)限校驗的過程
Spring Security過濾器鏈中,AuthorizationFilter的authorizationManager是我們要找的組件,該組件的check方法已被棄用,推薦使用authorize方法,最終通過接口路徑和權(quán)限進(jìn)行校驗,本文給大家介紹SpringSecurity實現(xiàn)動態(tài)權(quán)限校驗的相關(guān)知識,感興趣的朋友一起看看吧2025-02-02