【源码分析-Spring Boot】-2.Spring Boot 是如何解析配置注解的


Spring Boot 的配置解析: 【从零开始学Spring Boot】-2.Spring Boot ConfigurationProperties 配置

1.@ConfigurationProperties 是如何生效的?

@SpringBootApplication 注解是一个复合注解,它里面包含一个 @ConfigurationPropertiesScan,这个里面又有一个 @EnableConfigurationProperties,@ConfigurationProperties 的作用与它有关。

@ConfigurationProperties 中通过 @Import 引入一个 EnableConfigurationPropertiesRegistrar,它里面有一个 registerBeanDefinitions 方法

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    registerInfrastructureBeans(registry);
    ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
    getTypes(metadata).forEach(beanRegistrar::register);
}

registerBeanDefinitions 调用一个 registerInfrastructureBeans ,这个方法将 属性绑定后置处理器、bean 校验器、元数据注入到 registry 中,这里的 registry 保存了所有 bean 信息。

static void registerInfrastructureBeans(BeanDefinitionRegistry registry) {
    ConfigurationPropertiesBindingPostProcessor.register(registry);
    ConfigurationPropertiesBeanDefinitionValidator.register(registry);
    ConfigurationBeanFactoryMetadata.register(registry);
}

通过查看类图可以知道,ConfigurationPropertiesBindingPostProcessor 是 BeanPostProcessor 的一个实现类

它在 bean 实例化的时候发生作用,BeanPostProcessor 提供了 postProcessBeforeInitialization 和

postProcessAfterInitialization 两个方法

public interface BeanPostProcessor {

    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

在 ConfigurationPropertiesBindingPostProcessor 的 postProcessBeforeInitialization 方法中提供了对于属性值的注入

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 属性绑定
    bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
    return bean;
}

在 bind 方法中,通过 ConfigurationPropertiesBinder 来绑定 ConfigurationProperties 中属性

BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
    Bindable<?> target = propertiesBean.asBindTarget();
    // 获取目标 bean 上的 @ConfigurationProperties 注解
    ConfigurationProperties annotation = propertiesBean.getAnnotation();
    // 获取 BindHandler
    BindHandler bindHandler = getBindHandler(target, annotation);
    // 通过配置的 prefix 和 BindHandler 进行属性绑定
    return getBinder().bind(annotation.prefix(), target, bindHandler);
}

到这里已经比较清晰了,后面的就是从 应用上下文中获取属性值,然后转换成对应的类型,再将属性值设置给目标对象。

2.@Value 是如何生效的?

这个流程中,doCreateBean 前面的流程实际上是 spirng bean 的初始化流程,在初始化过程中,会对 bean 的依赖和字段进行填充;BeanPostProcessor 也是在这个阶段发生作用

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
            if (filteredPds == null) {
                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                return;
            }
        }
        pvs = pvsToUse;
    }
}

使用注解进行 bean 注入的时候,会有一个 AutowiredAnnotationBeanPostProcessor 的处理类,它里面有一个 postProcessProperties 方法

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

InjectionMetadata 是类的注入元数据,这里通过它来对 bean 中的属性进行注入,它里面提供了多种注入元件,而 ConfigurationProperties 主要通过字段属性进行注入

AutowiredFieldElement 的 inject 方法实现如下

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    // 判断是否已缓存,如果缓存了,直接获取
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
        // 如果没有缓存,需要从 beanFactory 中获取具体值,然后缓存起来
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        synchronized (this) {
            if (!this.cached) {
                if (value != null || this.required) {
                    this.cachedFieldValue = desc;
                    registerDependentBeans(beanName, autowiredBeanNames);
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName) &&
                            beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                            // 将获取到的值缓存起来
                            this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                desc, autowiredBeanName, field.getType());
                        }
                    }
                }
                else {
                    this.cachedFieldValue = null;
                }
                // 修改标记
                this.cached = true;
            }
        }
    }
    if (value != null) {
        // 最终将获取到的值,通过反射进行注入
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

接下来调用流程是 resolveDependency -> doResolveDependency -> resolveEmbeddedValue

@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
    if (value == null) {
        return null;
    }
    String result = value;
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
        result = resolver.resolveStringValue(result);
        if (result == null) {
            return null;
        }
    }
    return result;
}

最后调用到 PropertyPlaceholderConfigurer,通过解析配置文件获取到最终值

@Override
@Nullable
public String resolveStringValue(String strVal) throws BeansException {
    String resolved = this.helper.replacePlaceholders(strVal, this.resolver);
    if (trimValues) {
        resolved = resolved.trim();
    }
    return (resolved.equals(nullValue) ? null : resolved);
}

文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
 上一篇
【从零开始学Spring Boot】-3.Spring Boot WebMVC 【从零开始学Spring Boot】-3.Spring Boot WebMVC
1.简介1.1 概述 The Spring portfolio provides two parallel stacks. One is based on a Servlet API with Spring MVC and Spring D
下一篇 
【从零开始学Spring Boot】-2.Spring Boot ConfigurationProperties 配置 【从零开始学Spring Boot】-2.Spring Boot ConfigurationProperties 配置
1.简介1.1 概述 Annotation for externalized configuration. Add this to a class definition or a @Bean method in a @Configurati
  目录