Spring源码解读之BeanFactoryPostProcessor的处理
2020-12-13 04:19
标签:bean gap 应该 pos 正是 teacher nbsp startup tar 前言 前段时间旁听了某课堂两节Spring源码解析课,刚好最近自己又在重新学习中,便在这里记录一下学习所得。我之前写过一篇博文,是介绍BeanFactoryPostProcessor跟BeanPostProcessor是如何发挥作用的,当时觉得讲的还行,但是现在看来,太粗劣了,很多地方没涉及到,而且重点都被我忽略了,简直就是蠢得不行。现在就用这篇文章弥补一下前文中对BeanFactoryPostProcessor的讲解,争取把重点讲到,至于BeanPostProcessor,由于涉及到的东西太多,限于本人目前的水平只能作罢,待后面感悟成熟了再来补充。 我们以AnnotationConfigApplicationContext为例来构建测试类,先附上此次打断点调试的三个简约到极致的测试类: 1、洞悉启动容器时的准备工作 熟悉一些Spring的道友应该都知道,refresh方法中的invokeBeanFactoryPostProcessors方法实现了对BeanFactoryPostProcessor实现类的处理。大家如果只看invokeBeanFactoryPostProcessors方法的话,不会发现有何异常之处,此方法虽然较长,但是处理逻辑很清晰,先对重写了BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor方法的实现类进行处理,后对重写了BeanFactoryPostProcessor的方法的实现类做了处理。但是如果心细的话,你会发现问题,Spring是如何将@ComponentScan("myPackage")注解发挥作用的?这时带着这样的问题,我们再回过头来看容器的构造方法,就会在这平实的表面下发现意想不到的 "杀机"。 通过这个构造方法可以知道,在第二行将我们的测试类ScanConfig 注册进了容器中,但这只是注册,注册之后是如何调用如何实现了@ComponentScan("myPackage")这个注解的包扫描的呢?这时我们将目光锁定this()方法。点进去后发现是这样的: 在第二行新建reader对象时,调用了这个构造方法: 其中的第六行,最终调用了AnnotationConfigUtils#registerAnnotationConfigProcessors方法,而就是在这个方法中完成了对多个重要Bean的注册,跟我们关系比较大的有以下几个: 其中第一个对应的类就是我们重点关注的对象 ConfigurationClassPostProcessor类,查看此类的组成,发现它实现了BeanDefinitionRegistryPostProcessor接口,而此接口正是BeanFactoryPostProcessor的子接口。此时,萦绕在我们心头的迷雾开始渐渐散开,我们仿佛能抓到一闪而过的逻辑走向,现在让我们带着之前的发现,进入正主invokeBeanFactoryPostProcessors方法中一探究竟。 2、invokeBeanFactoryPostProcessors 该方法位于AbstractApplicationContext的refresh方法中,如下所示: 即第17行调用的方法。初学者看到这个方法的内部实现时,会发现此方法无外乎是找到所有实现了BeanDefinitionRegistryPostProcessor跟BeanFactoryPostProcessor接口的类,按照优先级(实现了PriorityOrdered接口的先于实现了Ordered接口,前两者均先于未实现的,且同一类中按照getOrder方法返回值排优先级)顺序执行它们的重写方法,先执行BeanDefinitionRegistryPostProcessor的重写方法,再执行BeanFactoryPostProcessor的重写方法,很容易理解的逻辑。但是现在我们是带着前面准备工作中得到的线索来的,此时再看,就能透过这个方法朴实的外表发现它潜藏的凶险,它如平地一声雷般让人猛地惊出一身冷汗。 我们打断点进入PostProcessorRegistrationDelegate类中的下面方法中 先略过开始对手动添加进去的beanFactoryPostProcessors处理逻辑,看后面的部分代码(由于此方法代码较多,此处就不全部粘贴出来了,因为逻辑很好理解,所以只粘贴重点): debug到第一行的时候,会发现此处获取到的postProcessorNames 中只有一个值,就是前面准备工作中通过硬编码往容器里注册的ConfigurationClassPostProcessor类。下面的逻辑就是进行各种判断,最后在第19行完成了对ConfigurationClassPostProcessor中postProcessBeanDefinitionRegistry方法的调用。 就是在这个后置处理方法中,完成了@ComponentScan("myPackage")中对包的扫描,完成了所有Bean的注册。执行完这个方法后,你会发现beanDefinitionMap中所有应该容器管理的类全都齐活了,包括其他的后置处理器。这样,后面继续调用beanFactory.getBeanNamesForType方法时,获取到的是所有满足条件的类,后面的工作就会有条不紊的开展下去了。 总结 本文着重追溯了BeanFactoryPostProcessor及其子接口是如何在Spring中发挥作用的。先通过Spring初始化容器时注册进去的ConfigurationClassPostProcessor类触发对其他类的扫描,待全部注册进容器后,再从容器中取对应的BeanFactoryPostProcessor及其子接口的实现类,逐一对重写方法进行调用。 虽然魔鬼在细节,但这也正是解读源码的快乐之处,不是吗? Spring源码解读之BeanFactoryPostProcessor的处理 标签:bean gap 应该 pos 正是 teacher nbsp startup tar 原文地址:https://www.cnblogs.com/zzq6032010/p/11031214.html1 public class SpringTest {
2
3 public static void main(String[] args) {
4 // 从这两行代码,实地跟踪考察Spring中的流程
5 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
6 applicationContext.getBean(Teacher.class).teach();
7 }
8 }
1 package myPackage;
2 import org.springframework.stereotype.Service;
3
4 @Service
5 public class Teacher {
6 public Teacher () {
7 System.out.println("Teacher init");
8 }
9 public void teach () {
10 System.out.println("teach");
11 }
12 }
1 package myPackage;
2 import org.springframework.context.annotation.ComponentScan;
3
4 @ComponentScan("myPackage")
5 public class ScanConfig {
6 }
1 public AnnotationConfigApplicationContext(Class>... annotatedClasses) {
2 this();
3 register(annotatedClasses);
4 refresh();
5 }
1 public AnnotationConfigApplicationContext() {
2 this.reader = new AnnotatedBeanDefinitionReader(this);
3 this.scanner = new ClassPathBeanDefinitionScanner(this);
4 }
1 public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
2 Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
3 Assert.notNull(environment, "Environment must not be null");
4 this.registry = registry;
5 this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
6 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
7 }
1 // BeanDefinitionHolder只是存放BD的,里面有三个属性:BD对象、beanName以及别名组成的String[]
2 Set
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 // Prepare this context for refreshing.
4 prepareRefresh();
5
6 // Tell the subclass to refresh the internal bean factory.
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8
9 // Prepare the bean factory for use in this context.
10 prepareBeanFactory(beanFactory);
11
12 try {
13 // Allows post-processing of the bean factory in context subclasses.
14 postProcessBeanFactory(beanFactory);
15
16 // Invoke factory processors registered as beans in the context.
17 invokeBeanFactoryPostProcessors(beanFactory);
1 public static void invokeBeanFactoryPostProcessors(
2 ConfigurableListableBeanFactory beanFactory, List
1 String[] postProcessorNames =
2 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
3
4 // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
5 List
下一篇:Ext.Net专题发布
文章标题:Spring源码解读之BeanFactoryPostProcessor的处理
文章链接:http://soscw.com/essay/29337.html