欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解@ConfigurationProperties實(shí)現(xiàn)原理與實(shí)戰(zhàn)

 更新時(shí)間:2020年10月10日 09:20:45   作者:cmlbeliever  
這篇文章主要介紹了詳解@ConfigurationProperties實(shí)現(xiàn)原理與實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

在SpringBoot中,當(dāng)需要獲取到配置文件數(shù)據(jù)時(shí),除了可以用Spring自帶的@Value注解外,SpringBoot提供了一種更加方便的方式:@ConfigurationProperties。只要在bean上添加上這個(gè)注解,指定好配置文件的前綴,那么對(duì)應(yīng)的配置文件數(shù)據(jù)就會(huì)自動(dòng)填充到bean中。舉個(gè)栗子,現(xiàn)在有如下配置:

myconfig.name=test
myconfig.age=22
myconfig.desc=這是我的測(cè)試描述

添加對(duì)應(yīng)的配置類,并添加上注解@ConfigurationProperties,指定前綴為myconfig

@Component
@ConfigurationProperties(prefix = "myconfig")
public class MyConfig {
private String name;
private Integer age;
private String desc;
  //get/set 略
  @Override
public String toString() {
	return "MyConfig [name=" + name + ", age=" + age + ", desc=" + desc + "]";
}
}

添加使用:

public static void main(String[] args) throws Exception {
	SpringApplication springApplication = new SpringApplication(Application.class);
	// 非web環(huán)境
	springApplication.setWebEnvironment(false);
	ConfigurableApplicationContext application = springApplication.run(args);

	MyConfig config = application.getBean(MyConfig.class);
	log.info(config.toString());
	application.close();
}

可以看到輸出log

com.cml.chat.lesson.lesson3.Application - MyConfig [name=test, age=22, desc=這是我的測(cè)試描述]

對(duì)應(yīng)的屬性都注入了配置中的值,而且不需要其他操作。是不是非常神奇?那么下面來(lái)剖析下@ConfigurationProperties到底做了啥?

首先進(jìn)入@ConfigurationProperties源碼中,可以看到如下注釋提示:

enter image description here

See Also 中給我們推薦了ConfigurationPropertiesBindingPostProcessor,EnableConfigurationProperties兩個(gè)類,EnableConfigurationProperties先放到一邊,因?yàn)楹竺娴奈恼轮袝?huì)詳解EnableXX框架的實(shí)現(xiàn)原理,這里就先略過。那么重點(diǎn)來(lái)看看ConfigurationPropertiesBindingPostProcessor,光看類名是不是很親切?不知上篇文章中講的BeanPostProcessor還有印象沒,沒有的話趕緊回頭看看哦。

ConfigurationPropertiesBindingPostProcessor
一看就知道和BeanPostProcessor有扯不開的關(guān)系,進(jìn)入源碼可以看到,該類實(shí)現(xiàn)的BeanPostProcessor和其他多個(gè)接口:

public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
	BeanFactoryAware, EnvironmentAware, ApplicationContextAware, InitializingBean,
	DisposableBean, ApplicationListener<ContextRefreshedEvent>, PriorityOrdered 

這里是不是非常直觀,光看類的繼承關(guān)系就可以猜出大概這個(gè)類做了什么。
BeanFactoryAware,EnvironmentAware,ApplicationContextAware是Spring提供的獲取Spring上下文中指定對(duì)象的方法而且優(yōu)先于BeanPostProcessor調(diào)用,至于如何工作的后面的文章會(huì)進(jìn)行詳解,這里只要先知道下作用就可以了。
此類同樣實(shí)現(xiàn)了InitializingBean接口,從上篇文章中已經(jīng)知道了InitializingBean是在BeanPostProcessor.postProcessBeforeInitialization之后調(diào)用,那么postProcessBeforeInitialization目前就是我們需要關(guān)注的重要入口方法。

先上源碼看看:

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
	//直接通過查找添加了ConfigurationProperties注解的的類
	ConfigurationProperties annotation = AnnotationUtils
			.findAnnotation(bean.getClass(), ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
	//查找使用工廠bean中是否有ConfigurationProperties注解
	annotation = this.beans.findFactoryAnnotation(beanName,
			ConfigurationProperties.class);
	if (annotation != null) {
		postProcessBeforeInitialization(bean, beanName, annotation);
	}
	return bean;
}

private void postProcessBeforeInitialization(Object bean, String beanName,
		ConfigurationProperties annotation) {
	Object target = bean;
	PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
			target);
	factory.setPropertySources(this.propertySources);
	factory.setValidator(determineValidator(bean));
	// If no explicit conversion service is provided we add one so that (at least)
	// comma-separated arrays of convertibles can be bound automatically
	factory.setConversionService(this.conversionService == null
			? getDefaultConversionService() : this.conversionService);
	if (annotation != null) {
		factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
		factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
		factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
		factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
		if (StringUtils.hasLength(annotation.prefix())) {
			factory.setTargetName(annotation.prefix());
		}
	}
	try {
		factory.bindPropertiesToTarget();
	}
	catch (Exception ex) {
		String targetClass = ClassUtils.getShortName(target.getClass());
		throw new BeanCreationException(beanName, "Could not bind properties to "
				+ targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
	}
}

在postProcessBeforeInitialization方法中,會(huì)先去找所有添加了ConfigurationProperties注解的類對(duì)象,找到后調(diào)用postProcessBeforeInitialization進(jìn)行屬性數(shù)據(jù)裝配。

那么現(xiàn)在可以將實(shí)現(xiàn)拆分成如何尋找和如何裝配兩部分來(lái)說明,首先先看下如何查找到ConfigurationProperties注解類。

查找ConfigurationProperties

在postProcessBeforeInitialization方法中先通過AnnotationUtils查找類是否添加了@ConfigurationProperties注解,然后再通過 this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);繼續(xù)查找,下面詳解這兩步查找的作用。

AnnotationUtils

AnnotationUtils.findAnnotation(bean.getClass(),ConfigurationProperties.class);這個(gè)是Spring中常用的工具類了,通過反射的方式獲取類上的注解,如果此類添加了注解@ConfigurationProperties那么這個(gè)方法會(huì)返回這個(gè)注解對(duì)象和類上配置的注解屬性。

beans.findFactoryAnnotation

這里的beans是ConfigurationBeanFactoryMetaData對(duì)象。在Spring中,可以以工廠bean的方式添加bean,這個(gè)類的作用就是在工程bean中找到@ConfigurationProperties注解。下面分析下實(shí)現(xiàn)過程:

ConfigurationBeanFactoryMetaData

public class ConfigurationBeanFactoryMetaData implements BeanFactoryPostProcessor {

private ConfigurableListableBeanFactory beanFactory;

private Map<String, MetaData> beans = new HashMap<String, MetaData>();

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
		throws BeansException {
	this.beanFactory = beanFactory;
 //迭代所有的bean定義,找出那些是工廠bean的對(duì)象添加到beans中
	for (String name : beanFactory.getBeanDefinitionNames()) {
		BeanDefinition definition = beanFactory.getBeanDefinition(name);
		String method = definition.getFactoryMethodName();
		String bean = definition.getFactoryBeanName();
		if (method != null && bean != null) {
			this.beans.put(name, new MetaData(bean, method));
		}
	}
}

public <A extends Annotation> Map<String, Object> getBeansWithFactoryAnnotation(
		Class<A> type) {
	Map<String, Object> result = new HashMap<String, Object>();
	for (String name : this.beans.keySet()) {
		if (findFactoryAnnotation(name, type) != null) {
			result.put(name, this.beanFactory.getBean(name));
		}
	}
	return result;
}

public <A extends Annotation> A findFactoryAnnotation(String beanName,
		Class<A> type) {
	Method method = findFactoryMethod(beanName);
	return (method == null ? null : AnnotationUtils.findAnnotation(method, type));
}

//略...
	
private static class MetaData {
	private String bean;
	private String method;
  //構(gòu)造方法和其他方法略...
}

}

通過以上代碼可以得出ConfigurationBeanFactoryMetaData的工作機(jī)制,通過實(shí)現(xiàn)BeanFactoryPostProcessor,在回調(diào)方法postProcessBeanFactory中,查找出所有通過工廠bean實(shí)現(xiàn)的對(duì)象,并將其保存到beans map中,通過方法findFactoryAnnotation可以查詢到工廠bean中是否添加了對(duì)應(yīng)的注解。那么這里的功能就是查找工廠bean中有添加@ConfigurationProperties注解的類了。

屬性值注入

通過上述步驟,已經(jīng)確認(rèn)了當(dāng)前傳入的bean是否添加了@ConfigurationProperties注解。如果添加了則下一步就需要進(jìn)行屬性值注入了,核心代碼在方法postProcessBeforeInitialization中:

private void postProcessBeforeInitialization(Object bean, String beanName,
		ConfigurationProperties annotation) {
	Object target = bean;
	PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
			target);
	//重點(diǎn),這里設(shè)置數(shù)據(jù)來(lái)源
	factory.setPropertySources(this.propertySources);
	factory.setValidator(determineValidator(bean));
	//設(shè)置轉(zhuǎn)換器
	factory.setConversionService(this.conversionService == null
			? getDefaultConversionService() : this.conversionService);
	if (annotation != null) {
	//將annotation中配置的屬性配置到factory中
	}
	try {
	  //這里是核心,綁定屬性值到對(duì)象中
		factory.bindPropertiesToTarget();
	}
	catch (Exception ex) {
	//拋出異常
	}
}

繼續(xù)跟進(jìn)factory.bindPropertiesToTarget方法,在bindPropertiesToTarget方法中,調(diào)用的是doBindPropertiesToTarget方法:

private void doBindPropertiesToTarget() throws BindException {
	RelaxedDataBinder dataBinder 
  //略...
  //1、獲取bean中所有的屬性名稱
  Set<String> names = getNames(relaxedTargetNames);
  //2、將屬性名稱和前綴轉(zhuǎn)換為配置文件的key值
  PropertyValues propertyValues = getPropertySourcesPropertyValues(names,relaxedTargetNames);
  //3、通過上面兩個(gè)步驟找到的屬性從配置文件中獲取數(shù)據(jù)通過反射注入到bean中
	dataBinder.bind(propertyValues);
	//數(shù)據(jù)校驗(yàn)
	if (this.validator != null) {
		dataBinder.validate();
	}
	//判斷數(shù)據(jù)綁定過程中是否有錯(cuò)誤
	checkForBindingErrors(dataBinder);
}

上面代碼中使用dataBinder.bind方法進(jìn)行屬性值賦值,源碼如下:

public void bind(PropertyValues pvs) {
	MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ?
			(MutablePropertyValues) pvs : new MutablePropertyValues(pvs);
	doBind(mpvs);
}
protected void doBind(MutablePropertyValues mpvs) {
	checkAllowedFields(mpvs);
	checkRequiredFields(mpvs);
	//進(jìn)行賦值
	applyPropertyValues(mpvs);
}
protected void applyPropertyValues(MutablePropertyValues mpvs) {
	try {
		// Bind request parameters onto target object.
		getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
	}
	catch (PropertyBatchUpdateException ex) {
		// Use bind error processor to create FieldErrors.
		for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
			getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
		}
	}
}

經(jīng)過以上步驟連續(xù)的方法調(diào)用后,最終調(diào)用的是ConfigurablePropertyAccessor.setPropertyValues使用反射進(jìn)行設(shè)置屬性值,到這里就不繼續(xù)深入了。想要繼續(xù)深入了解的可以繼續(xù)閱讀源碼,到最后可以發(fā)現(xiàn)調(diào)用的是AbstractNestablePropertyAccessor.processLocalProperty中使用反射進(jìn)行賦值。

上面的代碼分析非常清晰明了的解釋了如何查找@ConfigurationProperties對(duì)象和如何使用反射的方式進(jìn)行賦值。

總結(jié)

在上面的步驟中我們分析了@ConfigurationProperties從篩選bean到注入屬性值的過程,整個(gè)過程的難度還不算高,沒有什么特別的難點(diǎn),這又是一個(gè)非常好的BeanPostProcessor使用場(chǎng)景說明。
從本文中可以學(xué)習(xí)到BeanPostProcessor是在SpringBoot中運(yùn)用,以及如何通過AnnotationUtils與ConfigurationBeanFactoryMetaData結(jié)合對(duì)系統(tǒng)中所有添加了指定注解的bean進(jìn)行掃描。

到此這篇關(guān)于詳解@ConfigurationProperties實(shí)現(xiàn)原理與實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)@ConfigurationProperties原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)過濾敏感詞的示例代碼

    SpringBoot實(shí)現(xiàn)過濾敏感詞的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何利用SpringBoot實(shí)現(xiàn)過濾敏感詞功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動(dòng)手嘗試一下
    2022-08-08
  • springboot?使用mybatis查詢的示例代碼

    springboot?使用mybatis查詢的示例代碼

    這篇文章主要介紹了springboot?使用mybatis查詢功能,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • maven 使用assembly 進(jìn)行打包的方法

    maven 使用assembly 進(jìn)行打包的方法

    這篇文章主要介紹了maven 使用assembly 進(jìn)行打包的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解

    JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解

    這篇文章主要給大家介紹了關(guān)于JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • 五種單件模式之Singleton的實(shí)現(xiàn)方法詳解

    五種單件模式之Singleton的實(shí)現(xiàn)方法詳解

    本篇文章是對(duì)Singleton的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • 如何利用MyBatisX插件自動(dòng)生成代碼

    如何利用MyBatisX插件自動(dòng)生成代碼

    這篇文章主要介紹了如何利用MyBatisX插件自動(dòng)生成代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Spring?Bean中的六種作用域你了解嗎

    Spring?Bean中的六種作用域你了解嗎

    Bean的作用域是指Bean實(shí)例的生命周期及可見性范圍,Spring框架定義了6種作用域,本文就來(lái)和大家聊聊這6種作用域的定義與使用,希望對(duì)大家有所幫助
    2023-09-09
  • Java注解Annotation與自定義注解詳解

    Java注解Annotation與自定義注解詳解

    本文全面講述了Java注解Annotation與Java自定義注解及相關(guān)內(nèi)容,大家可以認(rèn)真看看
    2018-03-03
  • SpringBoot前端傳遞數(shù)組后端接收兩種常用的方法

    SpringBoot前端傳遞數(shù)組后端接收兩種常用的方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot前端傳遞數(shù)組后端接收兩種常用的方法,文中通過代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-04-04
  • 關(guān)于IDEA MybatisX插件的使用小技巧

    關(guān)于IDEA MybatisX插件的使用小技巧

    這篇文章主要介紹了關(guān)于MybatisX插件的使用小技巧,MybatisX是IDEA的一款第三方插件,可以極大地提升我們的開發(fā)效率,一起來(lái)看看吧
    2023-03-03

最新評(píng)論