1 介绍
一句话总结:
ConfigurationClassPostProcessor的作用是将BeanDefinitionRegistry中的所有BeanDefinition重新提取出来,然后寻找这些BeanDefinition是否是标记有下面6种注解,(因为这些注解类的内部可能还有的别的BeanDefinition),有的话就将它们提取出来,封装成BeanDefinition注册到BeanDefinitionRegistry中。
ConfigurationClassPostProcessor用于对@Configuration类进行解析。
实际上不止@Configuration,该处理器会对容器中含有以下注解的bean进行处理:
- @Configuration
- @Component:注意,@Service、@Controller等一些注释本身其实也是@Component
- @ComponentScan
- @Import
- @ImportResource
- @PropertySource:会将其解析成PropertySource类然后存入environment中
这里还需要注意,第2-5点会被处理器标记属性为Lite,而@Configuration会被标记属性为Full。
功能:解析@Configuration等注释的类,将其所含有的其他bean注册到beanDefinitionMap中
该功能的对应方法是postProcessBeanDefinitionRegistry()
这里用代码例子进行简要的介绍:
先定义一个Config类
@Configuration
public class PeopleConfig {
@Bean(name="person1")
public Person person1() {
Person person= new Person();
person.setName("person1");
return person;
}
@Bean(name="person2")
public Person person2() {
Person person= new Person();
person.setName("person2");
return person;
}
public static void main(String[] args){
ApplicationContext context = new AnnotationConfigApplicationContext(PeopleConfig.class);
}
}
调用ConfigurationClassPostProcessor进行后置处理前,beanDefinitionMap含有PeopleConfig,但不含有person1和person2:
后置处理后,beanDefinitionMap含有了person1和person2这两个bean的beanDefinition:
实际上,除了@Configuration外,其他注解也是可以的,以@ComponentScan为例:
@ComponentScan("com.test")
public class PeopleConfig {
}
后置处理器操作前:
后置处理器操作后如下图所示,可以看到多了好几个beanDefinition:
而对于@PropertySource这个注解,会将注解中对应的文件解析成PropertySource中并放入environment中。
具体的原理见下方的“原理篇”。
2 原理
2.1 预备知识
阅读下面的文章之前,需要对Spring 的metadata有足够的了解,详见我的另一篇博客:
spring 之 元数据metadata
2.2 原理
关于ConfigurationClassPostProcessor,个人推荐下面这篇文章,作者有很用心地去介绍这个后置处理器:
ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!
补充:
2.2.1 文中也存在一些错误(个人观点),这里记录一下:
第2点 postProcessBeanDefinitionRegistry()的代码中,关于parser.parse(candidates);方法的注释有问题:这一步并不会将将加了@Configuration注解的类加到beanDefinitionMap中,然而在具体代码中,并没有看到这一步操作。
而对@Configuration类进行增强那段,因为我@Configuration用的比较简单,所以对这部分理解的不是很透彻。
2.2.2 @Configuration的情况
另外,对于@Configuration的情况,解析的方式是获得该类的AnnotationMetaData,然后再调用getAnnotatedMethods()获取@Bean的所有方法的MethodMetadata。
AnnotationMetadata original = sourceClass.getMetadata();
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());