持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
概述
上一篇中介绍了@Component注解,被它标记的类会被 Spring 的组件扫描逻辑筛选出来生成对应的 BeanDefinition,另外,@``Controller、@Service、@``Repository注解标记的类型也会被筛选,因为这几个注解都被标记了@Component元注解。除此之外,还有一个常见的注解@``Configuration会被 Spring 扫描到,它不仅用来生成 BeanDefinition,还用来进行 Spring 应用的配置。
本文主要分析@``Configuration中的配置是如何被读取和执行的。
@``Configuration注解
注解@``Configuration 的源码如下。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
因为它也被标记了@Component注解,因此,标记了@``Configuration注解的类也会被 Spring 识别到,并且生成其对应的 BeanDefinition。不过,@``Configuration的作用并非是用来配置 BeanDefinition 的,而是可以像一个 XML 配置文件一样,配置 Bean 的信息,或者其他的一些配置。
比如,可以如下这样配置一个单例 Bean 极其创建的逻辑:
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
然后,便可以在上下文中获取到名为user的对象。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext("com.pseudocode.labs");
User user = (User)context.getBean("user");
user.sayHello();
}
}
在 Spring 扫描包路径的时候,只会将扫描到的类型创建成对应的 BeanDefinition 并注册到容器中,那么,这里的配置是如何被加载到 Spring 容器中的呢?
后处理器的注册
这里需要回顾一下之前文章的内容,在《Spring 源码阅读 31:基于注解初始化 Spring 上下文的原理(1) 》中,曾经分析过,AnnotationConfigApplicationContext 初始化时,会初始化一个 AnnotatedBeanDefinitionReader 对象,初始化的过程中,会调用 AnnotationConfigUtils 的registerAnnotationConfigProcessors方法,其中,会向容器中注册一些后处理器类,这里面就包含了处理被标记了@``Configuration的类型的后处理器 ConfigurationClassPostProcessor。
// org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
我们看一下 ConfigurationClassPostProcessor 的继承关系。
这里可以注意到两点:
- 它实现了 BeanDefinitionRegistryPostProcessor 接口,而 BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,因此,它是一个处理 BeanFactory 的后处理器。这一点需要留意,后面会涉及到。
- 它实现了 PriorityOrdered 及 Ordered 接口,因此可以为它指定优先级和执行顺序。
至此可以得到结论,AnnotationConfigApplicationContext 默认包含了一个后处理器,能够处理被@``Configuration标记的类型中的配置内容,那么接下来的问题就是,这个后处理器中的处理方法是什么时候执行的?
后处理器的执行
在 Spring 初始化上下文的过程中,当处理完准备工作之后,会通过调用refresh方法进入到核心流程中,refresh方法被定义在 AbstractApplicationContext 抽象类中,也就是说,所有继承了 AbstractApplicationContext 的上下文类型的上下文类型,都执行了同样的refresh方法,包括基于注解的上下文类型 AnnotationConfigApplicationContext 和基于 XML 配置文件的上下文类型 ClassPathXmlApplicationContext。
在refresh方法中,完成 BeanFactory 的创建和预处理之后,会执行 BeanFactory 的后处理逻辑,具体对应 AbstractApplicationContext 的invokeBeanFactoryPostProcessors方法,具体的执行步骤,被委托给了 PostProcessorRegistrationDelegate 的invokeBeanFactoryPostProcessors方法。
具体的执行逻辑,在之前的源码分析文章中有过详细的介绍,可以参考:Spring 源码阅读 13:执行 BeanFactoryPostProcessor 中的处理方法 。
具体到 ConfigurationClassPostProcessor 后处理器,前面提到它实现了 BeanDefinitionRegistryPostProcessor,因此,它的postProcessBeanDefinitionRegistry方法会被先执行,之后它的postProcessBeanFactory会被执行。
因此,对@``Configuration标记的类型的处理都在这两个方法只中了。
总结
本文梳理了@``Configuration标记的配置类的配置信息,是由其对应的 BeanFactory 后处理器负责执行的,这个后处理器在上下文创建时被注册到容器中,当容器的预处理完成后会执行其中的后处理方法。之后的文章,会进入 ConfigurationClassPostProcessor 中,详细分析两个处理方法。