【源码】走一遍refresh()方法的源码就能弄懂Spring容器的创建过程

41 篇文章 8 订阅
26 篇文章 6 订阅

【源码】走一遍refresh()方法的源码就能弄懂Spring容器的创建过程

Spring框架的两大核心思想,一个是IOC控制反转,另一个就是AOP面向切面编程。而IOC控制反转指的是将创建对象的操作交给Spring容器来解决,容器同一对所有的对象进行管理,我们无需关心创建对象时,对象之间的依赖关系,只需要在使用对象时从容器中获取即可。要想从容器中获取对象来使用,那就必须首先创建出这个Spring容器,这篇文章就来从源码角度出发,来看看Spring容器的创建过程。

创建Bean可以使用XML配置文件,也可以使用注解的方式,而平时开发中使用注解来创建、管理Bean已经成为主流。接下来我们通过加载配置类的方式来创建一个基于注解的Spring容器。我们就以AnnotationConfigApplicationContext这个类为入口,来看看Spring容器到底是怎么被创建出来的。

我们说IOC容器就像一个“大箱子”,里面保存着大量的已经创建好的Bean对象,我们在使用的时候直接“开箱即用”。但是IOC在一开始创建的时候是空空如也的,它需要将一些Bean对象创建并保存起来。Java项目中这么多的类,不能一股脑的,把有用没用的类当看作是Bean对象全都给注册到容器里面来,那么IOC是如何识别该把哪些类封装成Bean注册到IOC容器里面来呢?这就需要特定的注解了。比如@Service、@Controller、@Repository等等,Spring需要一个注解配置读取器,读取加了特定注解的类,转换为BeanDefinition对象。这个BeanDefinition对象存储了bean对象的所有信息。这里你可以把注解配置读取器类比于JVM中的类加载器,BeanDefinition对象类比于JVM方法区中的类模板。光知道需要读取哪些标注了特定的注解类还不够,首先Spring容器需要先根据包目录找到这些类,所以还需要一个类路径扫描器。当我们根据类路径扫描器找到了标注特定的注解的类之后,我们就要将这个类通过注解配置读取器转换为BeanDefinition对象了,此时我们要想生成Bean对象,还需要有一个BeanFactory工厂,来帮助我们创建出Bean对象。

所以,根据上面我们说的,我们先来看一下这一行创建Spring容器的代码:

ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

我们加载了一个配置类,这个配置类配置了类路径扫描的包名,以及适应注解标注了需要注册哪些类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q1Skxldt-1632686549384)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927024852291.png)]

我们可以从IOC容器创建的源码中可以看到,首先是根据传入的配置类使用注解配置读取器读取配置类信息,来解析用户传入的 Spring 配置类,其实也是将配置类解析成一个 BeanDefinition 然后注册到容器中
在这里插入图片描述[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ho950Sxv-1632686549413)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927030144288.png)]

总结一下就是:IOC容器创建可以分为初始化容器+刷新容器

在调用IOC容器的构造方法初始化一个容器时,会传入一个配置类。基于注解的IOC容器会先执行父类初始化方法,实例化一个BeanFactory工厂、BeanDefinitionReader注解配置读取器、和一个ClassPathBeanDefinitionScanner扫描器,同时向容器中添加两个后置处理器,一个ConfigurationClassPostProcessor配置类后置处理器,它是一个BeanFactory后置处理器,用来完成Bean的扫描和注册工作;另一个是AutowiredAnnotationBeanPostProcessor自动装配有关的后置处理器,它是一个Bean后置处理器,用来自动注入标注了@Autowire注解的Bean。有了这俩个后置处理器,然后就会把配置类注册到容器中,因为配置类本身也使用的@Configuration注解进行了标识,所以配置类本身也是一个Bean。至此IOC容器的创建的第一阶段初始化容器算是结束了。

我们通过源码可以看到实际上创建容器的核心是refresh()方法,是用来刷新容器的。当refresh()方法刷新完容器,那么这个Spring容器才算是真正的创建了出来。弄懂了refresh()方法你也就弄懂了容器的创建过程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nd2waDdZ-1632686549418)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926151848899.png)]

refresh()方法源码中总共包含了12个方法,也就是说容器的刷新有12个步骤。

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      //1.刷新前的预处理
      prepareRefresh();
       
      // Tell the subclass to refresh the internal bean factory.
      //2.获取 beanFactory,即前面创建的【DefaultListableBeanFactory】
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      //3.预处理 beanFactory,向容器中添加一些组件
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         //4.子类通过重写这个方法可以在 BeanFactory 创建并与准备完成以后做进一步的设置
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         //5.执行 BeanFactoryPostProcessor 方法,beanFactory 后置处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         //6.注册 BeanPostProcessors,bean 后置处理器
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         //7.初始化 MessageSource 组件(做国际化功能;消息绑定,消息解析)
         initMessageSource();

         // Initialize event multicaster for this context.
         //8.初始化事件派发器,在注册监听器时会用到
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         //9.留给子容器(子类),子类重写这个方法,在容器刷新的时候可以自定义逻辑,web 场景下会使用
         onRefresh();

         // Check for listener beans and register them.
         //10.注册监听器,派发之前步骤产生的一些事件(可能没有)
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         //11.初始化所有的单实例 bean
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         //12.发布容器刷新完成事件
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

接下来我们就一步一步的来分析refresh()方法

步骤1:刷新前的预处理

refresh()方法会在刷新前,调用prepareRefresh()方法进行刷新前的预处理工作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uW8dIQIR-1632686549425)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926153035523.png)]

prepareRefresh()方法的预处理工作主要包括,像是记录容器启动的时间,打印一些容器启动日志初始化一些属性并校验一些参数(有必要的话,留给子类去实现)等等,同时还可以创建一个早期应用事件链表,用来存放早期的一些应用事件,当后面事件派发器创建出来的时候,就会立即派发这些早期的事件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dyfFnbCd-1632686549429)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926155811515.png)]

步骤2:获取BeanFactory

refresh()方法执行完刷新前的预处理之后,会调用obtainFreshBeanFactory()方法获取BeanFactory

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YMDDr9eu-1632686549434)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926160620303.png)]

obtainFreshBeanFactory()方法会首先调用refreshBeanFactory()方法来刷新BeanFactory,会创建一个DefaultListableBeanFactory对象,并设置序列化id,用来唯一标识这个BeanFactory
在这里插入图片描述

然后obtainFreshBeanFactory()然后调用getBeanFactory()获取刚才创建出的DefaultListableBeanFactory实例对象,然后将这个BeanFactory对象返回
在这里插入图片描述

步骤3:预处理 beanFactory

在第二步我们获取到了BeanFactory,在使用BeanFactory之前我们需要对它进行一些预处理。比如设置BeanFactory的类加载器、各种后置处理器、以及自动装配的相关设置,这一步主要是给BeanFactory中添加一些组件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUkq4EFU-1632686549456)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926164401425.png)]
在这里插入图片描述
在这里插入图片描述

步骤4:BeanFactory的后置处理

refresh()方法调用postProcessBeanFactory(beanFactory)方法,可以在BeanFactory准备工作完成后进行后置处理工作。

子类通过重写这个方法来在BeanFactory创建并预准备完成以后来做进一步的设置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jtBFTUIr-1632686549460)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926165402007.png)]

步骤5:执行BeanFactory后置处理器

refresh()调用invokeBeanFactoryPostProcessors(beanFactory)执行BeanFactoryPostProcessor的方法

BeanFactoryPostProcessorBeanFactory后置处理器,它会在BeanFactory标准初始化之后执行,标准初始化指的就是前面的前4步。也就是说,在BeanFactory准备好后,并且进行完预处理以及后置处理以后,就会执行BeanFactory的后置处理器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XBbEIpg-1632686549466)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926202617666.png)]

首先会调用getBeanFactoryPostProcessors()方法获取到所有的BeanFactoryPostProcessor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w1RCQgQt-1632686549469)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926203713824.png)]
正是因为有BeanFactoryPostProcessor这个接口的存在,才可以使得我们在Spring扫描完所有的bean转成BeanDefinition的时候,可以做一些自定义的后置操作。

BeanFactoryPostProcessor这个接口又有一个子接口BeanDefinitionRegistryPostProcessor

在这里插入图片描述

BeanFactoryPostProcessor这个接口可以把ConfigurableListableBeanFactory暴露给我们,使得我们可以对BeanFactory进行一些后置处理。而子接口BeanDefinitionRegistryPostProcessor可以把BeanDefinitionRegistry这个注册器暴露给我们,使得我们可以获取到注册器,进行一些后置处理,比如按照我们的实际需要动态的注册Bean。

在获取到所有的获取到所有的BeanFactoryPostProcessor之后:

(1)先执行BeanDefinitionRegistryPostProcessor这个Bean定义注册器的后置处理器

首先判断当前的BeanFactory是否是BeanDefinitionRegistry

在这里插入图片描述

如果是,那么就会遍历所有的BeanFactoryPostProcessor,判断是否是对应的BeanDefinitionRegistryPostProcessor类型,并加入到集合中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s2bLZ1F9-1632686549471)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926205601526.png)]

1)首先处理实现了PriorityOrdered优先级接口的BeanDefinitionRegistryPostProcessor后置处理器会回调它对应的后置处理方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Z9bwPEZ-1632686549475)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926210100592.png)]
在这里插入图片描述

2)然后处理实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-snanQUar-1632686549476)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926215310957.png)]
3)最后处理没有实现任何接口的常规BeanDefinitionRegistryPostProcessor
在这里插入图片描述

(2)执行完BeanDefinitionRegistryPostProcessor之后,再执行BeanFactoryPostProcessor

获取到BeanFactoryPostProcessor类型的beanFactoryPostProcessor后置处理器
在这里插入图片描述

接下来和上面执行BeanDefinitionRegistryPostProcessor类型的后置处理器一样,也是先处理实现了PriorityOrdered接口BeanFactoryPostProcessor,再处理实现了Ordered接口的BeanFactoryPostProcessor,最后处理没有实现任何接口的BeanFactoryPostProcessor
在这里插入图片描述
按照实现的接口分为,分别回调后置处理器对应的后置处理方法
在这里插入图片描述

步骤6:向容器中注册Bean后置处理器

refresh()调用registerBeanPostProcessors(beanFactory)方法,注册Bean的后置处理器,后置处理器是用来拦截bean创建过程的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dByQKjpu-1632686549486)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926221735976.png)]

不同接口类型的BeanPostProcessor,即继承了BeanPostProcessor接口的不同子接口,在Bean创建前后的执行时机是不一样的

首先获取所有的BeanPostProcessor

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RpGdBRyJ-1632686549488)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926230025376.png)]

Bean的后置处理器也是按照实现PriorityOrdered接口、Ordered接口、没有实现任何接口进行分类,并加入到对应的集合中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tMEWzydH-1632686549489)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926230302763.png)]

1)分好类之后,也是先注册实现了priorityOrdered优先级接口的BeanPostProcessor,调用registerBeanPostProcessors()方法进行注册
在这里插入图片描述
registerBeanPostProcessors()方法调用addBeanPostProcessor()方法将BeanPostProcessor添加到BeanFactory
在这里插入图片描述

2)再注册实现了Ordered优先级接口的BeanPostProcessor
在这里插入图片描述
3)最后注册没有实现任何优先级接口的BeanPostProcessor
在这里插入图片描述

步骤7:初始化MessageSource组件

refresh()调用initMessageSource()初始化MessageSource组件,这个组件用来处理国际化,以及消息绑定和消息解析
在这里插入图片描述

步骤8:初始化事件派发器

refresh()方法调用initApplicationEventMulticaster()方法初始化事件派发器
在这里插入图片描述

在整个容器创建过程中,Spring 会发布很多容器事件,如容器启动、刷新、关闭等,这个功能的实现依赖于ApplicationEventMulticaster 广播器组件,通过它来派发事件通知。

首先获取BeanFactory
在这里插入图片描述
然后从BeanFactoryidapplicationEventMulticaster且类型是ApplicationEventMulticaster的事件派发器,也就说会先判断是否有自定义的事件派发器

在这里插入图片描述

如果上一步容器中没有配置事件派发器,就会创建一个SimpleApplicationEventMulticaster类型的事件派发器,并将这个事件派发器添加到BeanFactory中,以后其它组件可以直接自动注入来使用
在这里插入图片描述

步骤9:调用onRefresh()自定义容器刷新时逻辑

onRefresh()这个方法和步骤4中的postProcessBeanFactory()一样,都是留给子容器(子类)的,子类重写这个方法,可以在容器刷新的时候可以自定义逻辑,一般web场景下会使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3L7crzNS-1632686549500)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926234657826.png)]

步骤10:注册监听器

refresh()调用registerListeners()方法,将项目中所有的ApplicationListener监听器注册到容器中。如果用户想监听容器事件,需要实现 ApplicationListener 接口并放入到容器中,在这里会被 Spring 扫描到,添加到 ApplicationEventMulticaster 广播器里,以后就可以发布事件通知,对应的 Listener 就会收到消息进行处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GpRelIcs-1632686549501)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927000816946.png)]

首选从容器中获取到所有的ApplicationListener,并将每一个监听器添加到事件派发器中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dsELn42k-1632686549506)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927001540874.png)]
我们在步骤1中刷新前预处理阶段,已经创建了一个保存早期事件的链表,并且在步骤8中已经初始化了事件派发器,在注册监听器的这一阶段,如果有一些早期的事件,就会直接先派发这些早期事件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WhoeQy48-1632686549509)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927001901619.png)]

步骤11:初始化所有的单实例Bean

refresh()方法调用finishBeanFactoryInitialization()方法,初始化剩下的所有单实例bean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a6Udhnr0-1632686549511)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927002714925.png)]

在上面的步骤中,Spring 的大多数组件都已经初始化完毕了,剩下来的这个步骤就是初始化所有剩余的单实例 bean,在Spring中初始化一个 bean 对象是非常复杂的,如循环依赖、bean后置处理器运用、aop 代理等。执行完这个方法,容器里面的bean就都初始化完毕了。

finishBeanFactoryInitialization()方法先做一些BeanFactory相关的设置,实际上是调用preInstantiateSingletons()方法实例化剩下的所有单实例bean

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SIvkWWFV-1632686549514)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927003538005.png)]

首先获取到所有定义的bean,然后依次进行创建和初始化对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Slk5kqI4-1632686549515)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927004247947.png)]

然后遍历所有的bean,获取bean的定义信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aJtdBFfg-1632686549516)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927004849684.png)]

接着会判断当前bean是否不是抽象的,且是否是单实例的,而且是否不是懒加载的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2bYJ6wL4-1632686549524)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927005007194.png)]

如果是,则进一步判断当前bean是否是FactoryBean,即判断是否实现了FactoryBean接口的bean,如果是工厂bean,就会调用接口中getObject()方法来创建对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQGaihoj-1632686549526)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927005405639.png)]

如果不是工厂bean就会调用getBean()方法来创建对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TJzOQKux-1632686549532)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927005435155.png)]

步骤12:发布容器刷新完成事件

refresh()方法调用finishRefresh()方法完成BeanFactory的初始化创建工作,IOC容器就创建完成了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqidQA39-1632686549534)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927013908151.png)]

整个容器初始化完毕之后,会在这里进行一些扫尾工作,比如初始化生命周期处理器,发布容器刷新完成事件等。

finishRefresh()方法首先调用initLifecycleProcessor()初始化和生命周期有关的处理器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dTtn1p5W-1632686549536)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927013959624.png)]

initLifecycleProcessor()方法默认从容器中找是否有自定义的idlifecycleProcessor且类型为LifecycleProcessor的组件,如果没有找到就会创建DefaultLifecycleProcessor并将其注册到容器中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2POZFN2r-1632686549538)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927014128917.png)]

然后调用publishEvent()方法发布容器刷新完成事件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnF4jIN5-1632686549541)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927014611847.png)]
最后调用registerApplicationContext()来暴露一些特定的Bean,并完成了容器的创建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUSr6iUx-1632686549545)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927014852046.png)]
上面的12个步骤就是容器刷新的核心流程!

稍微总结一下Spring容器创建的过程:

  1. Spring容器在启动的时候,会先保存所有注册你来的Bean的定义信息
  2. Spring会在合适的时机创建这些Bean。如果用到了某个Bean的时候,就会利用getBean()方法来创建这个Bean,创建好以后保存在容器中,方便给其它组件使用。然后在刷新容器的时候会统一创建剩下的所有Bean。
  3. Spring容器中存在各种类型的后置处理器,有BeanFactory的后置处理器,有Bean的后置处理器,不同的后置处理器执行的时机也不相同。每一个Bean创建完成,都会使用各种后置处理器来进行处理,增强Bean的功能。
  4. Spring容器中还有很多监听器,负责监听事件,事件派发器负责事件的派发。
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值