SpringBoot之淺析配置項解析(四)

我們在之前的文章中簡單的說了一下SpringBoot對於默認的配置文件的解析過程,在這一篇文章中我們再簡單的分析一下SpringBoot是怎麼將解析到的配置屬性信息設置到相應的Bean上的。既然是用SpringBoot的屬性配置方式,那麼我們在這裏會在對應的類上加上ConfigurationProperties和Component(或是和Component相同功能的)註解。我們定義的Bean如下:

@Component
@ConfigurationProperties(prefix ="person.info")
public class PersonInfoDomain {
    /**
     * 用戶名
     */
    private String userName;
}

配置文件如下:
application
其內容依次如下:

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(不要問我爲什麼知道這個方法。。。放一下調用鏈的截圖)
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;
    }

getBeanPostProcessors

    @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);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章