我們在之前的文章中簡單的說了一下SpringBoot對於默認的配置文件的解析過程,在這一篇文章中我們再簡單的分析一下SpringBoot是怎麼將解析到的配置屬性信息設置到相應的Bean上的。既然是用SpringBoot的屬性配置方式,那麼我們在這裏會在對應的類上加上ConfigurationProperties和Component(或是和Component相同功能的)註解。我們定義的Bean如下:
@Component
@ConfigurationProperties(prefix ="person.info")
public class PersonInfoDomain {
/**
* 用戶名
*/
private String userName;
}
配置文件如下:
其內容依次如下:
person.info.user-name=lisi
person:
info:
user-name: zhangzhang
person.info.user-name=zhangsan
person:
info:
user-name: lilisisi
首先問一個問題,如果是你來實現這樣的功能的話,你會在什麼時機來進行屬性值設置的功能呢?在創建Bean的時候進行屬性的設置應該是一個比較合適的時機吧?Spring也是這樣做的。在Spring中提供了各種不同功能的接口來讓我們在Bean創建的過程中做一些不同功能的擴展,我們先稱爲”生命週期”的接口(可以在這裏進行查看:Spring Bean的生命週期小析(一) Spring Bean的生命週期小析(二)),在Spring在有這樣一個類:AbstractAutowireCapableBeanFactory這個類主要的一個功能是串聯Spring的生命週期。我們先看一下這個類中的這個方法:applyBeanPostProcessorsBeforeInitialization(不要問我爲什麼知道這個方法。。。放一下調用鏈的截圖)
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
//這裏的getBeanPostProcessors()這個方法獲取到的是Spring 容器中實現BeanPostProcessor接口的Bean 在這個Bean中有一個Bean叫ConfigurationPropertiesBindingPostProcessor 從這個Bean的名字我們可以感覺這個Bean應該是和ConfigurationProperties相關的類,而事實也確是如此 其他的我們先不說了,放一下SpringBoot中內置的一些BeanPostProcessor 的Bean,我們直接進入到 ConfigurationPropertiesBindingPostProcessor 這個類中
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//獲取類上的ConfigurationProperties註解 用AnnotationUtils來對類上的註解進行處理是很方便,有興趣的可以看一下AnnotationUtils中的方法的源碼實現
ConfigurationProperties annotation = AnnotationUtils
.findAnnotation(bean.getClass(), ConfigurationProperties.class);
//如果類上有ConfigurationProperties 註解
if (annotation != null) {
//主要處理方法
postProcessBeforeInitialization(bean, beanName, annotation);
}
//方法上的ConfigurationProperties註解
annotation = this.beans.findFactoryAnnotation(beanName,
ConfigurationProperties.class);
if (annotation != null) {
postProcessBeforeInitialization(bean, beanName, annotation);
}
return bean;
}
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
private void postProcessBeforeInitialization(Object bean, String beanName,
ConfigurationProperties annotation) {
//我們需要設置屬性的目標bean
Object target = bean;
//新建一個PropertiesConfigurationFactory類 這個類來完成屬性的賦值的工作
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target);
//propertySources 1)
factory.setPropertySources(this.propertySources);
//驗證器
factory.setValidator(determineValidator(bean));
//轉換服務
factory.setConversionService(this.conversionService == null
? getDefaultConversionService() : this.conversionService);
if (annotation != null) {
//類上 ConfigurationProperties註解的信息
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();
}
}
對於1)處的PropertySources我們應該不陌生了,前面我們一直在提到這個類。我們看一下這個PropertySources是在什麼時候進行賦值的。在ConfigurationPropertiesBindingPostProcessor中有一個這樣的方法afterPropertiesSet:
public void afterPropertiesSet() throws Exception {
if (this.propertySources == null) {
//尋找PropertySources
this.propertySources = deducePropertySources();
}
//validator的賦值
if (this.validator == null) {
this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
}
//轉換服務
if (this.conversionService == null) {
this.conversionService = getOptionalBean(
ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME,
ConversionService.class);
}
}
private PropertySources deducePropertySources() {
//從Spring 容器中獲取 PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer();
if (configurer != null) {
//configurer.getAppliedPropertySources() 這裏獲取到的就是我們之前一直提到的MutablePropertySources
return new FlatPropertySources(configurer.getAppliedPropertySources());
}
//如果Spring 容器中 沒有PropertySourcesPlaceholderConfigurer的話 則從ConfigurableEnvironment中獲取
if (this.environment instanceof ConfigurableEnvironment) {
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment)
.getPropertySources();
return new FlatPropertySources(propertySources);
}
//還獲取不到的話,則新建一個MutablePropertySources
return new MutablePropertySources();
}
private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() {
if (this.beanFactory instanceof ListableBeanFactory) {
//從Spring 容器中獲取 PropertySourcesPlaceholderConfigurer
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Map<String, PropertySourcesPlaceholderConfigurer> beans = listableBeanFactory
.getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false,
false);
if (beans.size() == 1) {
return beans.values().iterator().next();
}
//如果有多於一個存在的話,則返回null
if (beans.size() > 1 && logger.isWarnEnabled()) {
}
}
return null;
}
OK,我們進入到PropertiesConfigurationFactory看一下factory.bindPropertiesToTarget();這個方法的內容:
public void bindPropertiesToTarget() throws BindException {
//propertySources不能爲null
Assert.state(this.propertySources != null, "PropertySources should not be null");
try {
this.hasBeenBound = true;
doBindPropertiesToTarget();
}
}
private void doBindPropertiesToTarget() throws BindException {
//targetName PropertiesConfigurationFactory註解上的前綴值
//target 目標bean
RelaxedDataBinder dataBinder = (this.targetName != null
? new RelaxedDataBinder(this.target, this.targetName)
: new RelaxedDataBinder(this.target));
//校驗器
if (this.validator != null
&& this.validator.supports(dataBinder.getTarget().getClass())) {
dataBinder.setValidator(this.validator);
}
//轉換服務
if (this.conversionService != null) {
dataBinder.setConversionService(this.conversionService);
}
//集合的最大值
dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
//自定義Binder 這裏是一個空實現
customizeBinder(dataBinder);
//下面這兩段代碼是獲取 Spring支持的配置的名字 支持格式超出你的想象 可以調試自己看一下
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
Set<String> names = getNames(relaxedTargetNames);
//獲取PropertyValues 重點要分析的
PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
relaxedTargetNames);
//屬性值的綁定工作
dataBinder.bind(propertyValues);
if (this.validator != null) {
//屬性值的校驗
dataBinder.validate();
}
checkForBindingErrors(dataBinder);
}