栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

【源码】走一遍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);

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

我们可以从IOC容器创建的源码中可以看到,首先是根据传入的配置类使用注解配置读取器读取配置类信息,来解析用户传入的 Spring 配置类,其实也是将配置类解析成一个 BeanDefinition 然后注册到容器中

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

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

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

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()方法进行刷新前的预处理工作

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

步骤2:获取BeanFactory

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

obtainFreshBeanFactory()方法会首先调用refreshBeanFactory()方法来刷新BeanFactory,会创建一个DefaultListableBeanFactory对象,并设置序列化id,用来唯一标识这个BeanFactory。

然后obtainFreshBeanFactory()然后调用getBeanFactory()获取刚才创建出的DefaultListableBeanFactory实例对象,然后将这个BeanFactory对象返回

步骤3:预处理 beanFactory

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



步骤4:BeanFactory的后置处理

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

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

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

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

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

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


正是因为有BeanFactoryPostProcessor这个接口的存在,才可以使得我们在Spring扫描完所有的bean转成BeanDefinition的时候,可以做一些自定义的后置操作。

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

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

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

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

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

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

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


2)然后处理实现了Ordered顺序接口的BeanDefinitionRegistryPostProcessor

3)最后处理没有实现任何接口的常规BeanDefinitionRegistryPostProcessor

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

获取到BeanFactoryPostProcessor类型的beanFactoryPostProcessor后置处理器

接下来和上面执行BeanDefinitionRegistryPostProcessor类型的后置处理器一样,也是先处理实现了PriorityOrdered接口BeanFactoryPostProcessor,再处理实现了Ordered接口的BeanFactoryPostProcessor,最后处理没有实现任何接口的BeanFactoryPostProcessor。

按照实现的接口分为,分别回调后置处理器对应的后置处理方法

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dByQKjpu-1632686549486)(C:UsersJianAppDataRoamingTyporatypora-user-imagesimage-20210926221735976.png)]

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

首先获取所有的BeanPostProcessor

Bean的后置处理器也是按照实现PriorityOrdered接口、Ordered接口、没有实现任何接口进行分类,并加入到对应的集合中

1)分好类之后,也是先注册实现了priorityOrdered优先级接口的BeanPostProcessor,调用registerBeanPostProcessors()方法进行注册

registerBeanPostProcessors()方法调用addBeanPostProcessor()方法将BeanPostProcessor添加到BeanFactory中

2)再注册实现了Ordered优先级接口的BeanPostProcessor

3)最后注册没有实现任何优先级接口的BeanPostProcessor

步骤7:初始化MessageSource组件

refresh()调用initMessageSource()初始化MessageSource组件,这个组件用来处理国际化,以及消息绑定和消息解析

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

refresh()方法调用initApplicationEventMulticaster()方法初始化事件派发器

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

首先获取BeanFactory

然后从BeanFactory中id为applicationEventMulticaster且类型是ApplicationEventMulticaster的事件派发器,也就说会先判断是否有自定义的事件派发器

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

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

onRefresh()这个方法和步骤4中的postProcessBeanFactory()一样,都是留给子容器(子类)的,子类重写这个方法,可以在容器刷新的时候可以自定义逻辑,一般web场景下会使用。

步骤10:注册监听器

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

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


我们在步骤1中刷新前预处理阶段,已经创建了一个保存早期事件的链表,并且在步骤8中已经初始化了事件派发器,在注册监听器的这一阶段,如果有一些早期的事件,就会直接先派发这些早期事件。

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

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

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

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

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2bYJ6wL4-1632686549524)(C:UsersJianAppDataRoamingTyporatypora-user-imagesimage-20210927005007194.png)]

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

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

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

refresh()方法调用finishRefresh()方法完成BeanFactory的初始化创建工作,IOC容器就创建完成了

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

finishRefresh()方法首先调用initLifecycleProcessor()来初始化和生命周期有关的处理器

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

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


最后调用registerApplicationContext()来暴露一些特定的Bean,并完成了容器的创建

上面的12个步骤就是容器刷新的核心流程!

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

  1. Spring容器在启动的时候,会先保存所有注册你来的Bean的定义信息
  2. Spring会在合适的时机创建这些Bean。如果用到了某个Bean的时候,就会利用getBean()方法来创建这个Bean,创建好以后保存在容器中,方便给其它组件使用。然后在刷新容器的时候会统一创建剩下的所有Bean。
  3. Spring容器中存在各种类型的后置处理器,有BeanFactory的后置处理器,有Bean的后置处理器,不同的后置处理器执行的时机也不相同。每一个Bean创建完成,都会使用各种后置处理器来进行处理,增强Bean的功能。
  4. Spring容器中还有很多监听器,负责监听事件,事件派发器负责事件的派发。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/270609.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号