SpringBoot中@ConfigurationProperties注解的使用與源碼詳解
前言
相信大家肯定了解@Value注解,它可以通過一個(gè)配置文件中的屬性名與對(duì)象的屬性進(jìn)行綁定。
@ConfigurationProperties注解的作用其實(shí)就類似于使用多個(gè)@Value注解同時(shí)綁定一個(gè)對(duì)象的多個(gè)屬性,@ConfigurationProperties注解用于自動(dòng)配置綁定,可以將application.properties配置中的值(準(zhǔn)確來說是Environment中的屬性值)注入到bean對(duì)象上,該注解的使用必須先將對(duì)象注入到IOC容器中才有配置自動(dòng)綁定的功能。
@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)未知字段時(shí)會(huì)出現(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;
}
配置文件中的屬性名稱需要與實(shí)體類的屬性保持一致,不然值會(huì)綁定不上,多個(gè)單詞可以使用橫杠進(jìn)行分割,SpringBoot會(huì)將橫杠命名轉(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實(shí)現(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實(shí)現(xiàn)了BeanPostProcessor接口,其postProcessBeforeInitialization()方法會(huì)在Bean實(shí)例化后執(zhí)行,在這里完成了配置文件的屬性與對(duì)象的屬性的綁定。
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來完成配置文件的屬性與對(duì)象的屬性的綁定
// 最終會(huì)調(diào)用Binder這個(gè)類來完成綁定
this.binder.bind(bean);
}
catch (Exception ex) {
throw new ConfigurationPropertiesBindException(bean, ex);
}
}
ConfigurationPropertiesBinder
ConfigurationPropertiesBinder主要負(fù)責(zé)構(gòu)建Binder并進(jìn)行緩存,SpringBoot啟動(dòng)過程中已經(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;
}
如何動(dòng)態(tài)刷新@ConfigurationProperties
如果配置中心配置更新了,遇到了@ConfigurationProperties標(biāo)注的配置bean,那么bean的屬性就不會(huì)自動(dòng)更新了,那么實(shí)現(xiàn)動(dòng)態(tài)更新@ConfigurationProperties標(biāo)注的bean的屬性呢?
如果使用的Nacos注冊(cè)中心,可以監(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)前最簡(jiǎn)單的辦法就是在bean上面加上@RefreshScope注解就能實(shí)現(xiàn)自動(dòng)刷新屬性值了。
到此這篇關(guān)于SpringBoot中@ConfigurationProperties注解的使用與源碼詳解的文章就介紹到這了,更多相關(guān)@ConfigurationProperties注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot @ConditionalOnMissingBean注解的作用詳解
- SpringBoot 注解事務(wù)聲明式事務(wù)的方式
- springboot2中使用@JsonFormat注解不生效的解決
- SpringBoot中@Pattern注解對(duì)時(shí)間格式校驗(yàn)方式
- springboot FeignClient注解及參數(shù)
- SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解決方案
- 親測(cè)SpringBoot參數(shù)傳遞及@RequestBody注解---踩過的坑及解決
- 詳解SpringBoot 快速整合Mybatis(去XML化+注解進(jìn)階)
- SpringBoot單元測(cè)試中@SpyBean使用小結(jié)
相關(guān)文章
講解Java設(shè)計(jì)模式編程中的建造者模式與原型模式
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的建造者模式與原型模式,設(shè)計(jì)模式有利于團(tuán)隊(duì)開發(fā)過程中的代碼維護(hù),需要的朋友可以參考下2016-02-02
解決rocketmq-spring-boot-starter導(dǎo)致的多消費(fèi)者實(shí)例重復(fù)消費(fèi)問題
這篇文章主要介紹了解決rocketmq-spring-boot-starter導(dǎo)致的多消費(fèi)者實(shí)例重復(fù)消費(fèi)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Java鏈表中元素刪除的實(shí)現(xiàn)方法詳解【只刪除一個(gè)元素情況】
這篇文章主要介紹了Java鏈表中元素刪除的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了java只刪除鏈表中一個(gè)元素的相關(guān)操作原理、實(shí)現(xiàn)方法與注意事項(xiàng),需要的朋友可以參考下2020-03-03
SpringBoot在一定時(shí)間內(nèi)限制接口請(qǐng)求次數(shù)的實(shí)現(xiàn)示例
在項(xiàng)目中,接口的暴露在外面,很多人就會(huì)惡意多次快速請(qǐng)求,本文主要介紹了SpringBoot在一定時(shí)間內(nèi)限制接口請(qǐng)求次數(shù)的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2022-03-03
Mybatis-plus實(shí)現(xiàn)join連表查詢的示例代碼
mybatis-plus在連表查詢上是不行的,如果需要連表查詢,就得乖乖的去寫xml文件了,本文介紹了mybatis-plus-join框架,它支持連表查詢,感興趣的可以了解一下2023-08-08

