阿里三面:说说你对Spring ioc启动原理的理解!

1. IOC概述

1.1 是什么?面试

两个概念:控制反转,依赖注入缓存

来看一下传统的干活方式:在对象单一职责原则的基础上,一个对象不多有不依赖其余对象而完成本身的工做,因此这个时候就会出现对象之间的依赖。而体如今咱们的开发中,就是须要什么对象的时候,就建立什么对象,此时对象建立的控制权在咱们本身手里。当对象建立的太多的时候,就会出现一个对象更改,就得更改全部依赖它的对象,耦合性大。自主性体现的同时也出现了对象耦合严重的状况架构

  • 这个时候,咱们就会思考,能不能咱们在用的时候直接拿到这个对象去用,而将建立对象的能力交给第三方,这样咱们就不须要关心对象是怎么建立的了。即将本身的控制权交出去。这就是控制反转
  • 这个时候,就会有另外一个问题产生了,对象怎么才能直接被咱们拿来用呢。对象建立的时候,咱们把这个对象注入到这个对象中,而后就可使用了。这就是依赖注入
  • 另外一个问题,耦合性怎么被解决掉的?经过控制反转咱们仅仅使用了这个对象,若是对象发生了修改,咱们仅仅须要修改第三方建立对象的方式便可,这个时候难道还会出现所谓的对象耦合吗?

2. IOC架构

阿里三面:说说你对Spring ioc启动原理的理解!

一个图搞定,这个就是IOC的架构思路,这不是其执行流程图app

咱们接下来一步一步来解读。ide

2.1 白话版函数

在第一章中咱们了解了IOC是来帮助咱们管理和建立对象的。微服务

这个时候咱们须要一个承载咱们须要建立信息的容器,即图中的XML或者注解,那么有了咱们本身的BeanDefiniton信息之后,咱们须要一个接口用来读取这些信息,因而出现了BeanDefinitionReader用来读取咱们本身的Bean信息。post

那么咱们须要考虑一个问题了,那么多的对象怎么生产呢?学习

答案就是工厂模式。Spring默认的工厂是DefaultListableBeanFactory,没错,Spring中的全部对象(容器对象和咱们本身建立的对象)都是由他建立的。大批量生产对象3d

这个时候又有了一个问题,咱们不想经过BeanFactory直接生产了,须要对这个工厂进行一些特定处理,因而出现了BeanFactoryPostProcessor,用来对工厂作一些特定的处理。咱们本身能够经过实现这个接口,进行自定义BeanFactory。又有兄弟说了:我想单首创建一些我喜欢的对象,安排,FactoryBean诞生了,它能够帮助咱们建立一个咱们须要的对象(第四部分详细解释他们之间的区别)。

那又有兄弟说了:我想让统一的对象建立以前按照个人方式进行一些特殊的行为,简单,安排:see_no_evil

BeanPostProcessor出现了,他提供了两个方法:一个在对象实例化以后初始化以前,执行内部的Before方法,在初始化以后,执行After方法。(Bean生命周期,第四部分详解

这个时候有兄弟有疑问了,不是说BeanPostProcessor在建立对象以前执行吗?怎么是建立完毕之后才执行的Before方法。

若是各位兄弟了解过指令重排序这个概念,那么必定会听过一个案例,建立一个对象须要三步

  • 建立空间(实例化)
  • 初始化
  • 赋值

其中在初始化和赋值会出现指令重排序

根据这个点,应该能够get到一个点,实例化和初始化不同。

因此又引出了一个点,咱们对Bean进行一些操做,怎么操做,确定是修改属性,或者添加一些属性等等,须要等待其在堆中开辟空间即实例化完成之后执行吧。

因此BeanPostProcessor的before方法在实例化以后执行,初始化以前执行。

经历过前面一大堆的操做之后,终于咱们的对象进入咱们兜里了(容器里)。

关于销毁,通常状况下咱们经过ApplicationContext拿不到其销毁方法,只能经过其子类实现获取,关于销毁一样的流程,先执行一个销毁以前的操做,而后再销毁。

其中在初始化和赋值会出现指令重排序

根据这个点,应该能够get到一个点,实例化和初始化不同。

因此又引出了一个点,咱们对Bean进行一些操做,怎么操做,确定是修改属性,或者添加一些属性等等,须要等待其在堆中开辟空间即实例化完成之后执行吧。

因此BeanPostProcessor的before方法在实例化以后执行,初始化以前执行。

经历过前面一大堆的操做之后,终于咱们的对象进入咱们兜里了(容器里)。

关于销毁,通常状况下咱们经过ApplicationContext拿不到其销毁方法,只能经过其子类实现获取,关于销毁一样的流程,先执行一个销毁以前的操做,而后再销毁。

2.2 实际工做流程

看过Spring源码或者听过的都知道里面有一个方法叫作refresh,他完成了好多事情。固然他的行为也表明了整个IOC容器加载和实例化对象的过程。第三章的代码解读中咱们仔细看

阿里三面:说说你对Spring ioc启动原理的理解!

执行过程:

  • 加载配置文件,初始化系统环境Environment接口
  • 准备上下文环境,初始化一些配置资源
  • 建立一个工厂
  • 为工厂添加各类环境
  • 获取子类本身重写的BeanFactoryPostProcessor
  • 执行容器和咱们本身的BeanFactoryPostProcessor
  • 注册BeanPostProcessor
  • 国际化处理
  • 转播器
  • 子类初始化Bean
  • 注册监听器,观察者模式
  • 完成Bean建立
  • 发布相应的事件,监听器

3. IOC源码解读

3.1 上下文配置启动

阿里三面:说说你对Spring ioc启动原理的理解!

在建立ClassPathXmlApplicationContext的时候,构造方法中执行了这些方法。

说白了,加载了一个解析配置文件路径的加载器;而后又经过系统环境变量拿到这个配置文件,进行一些配置文件的去空格,转换表达式等等操做(没有进行解析);最后就是那个被我标成红色东东,refresh方法中它完成了几乎全部的工做。下面细聊

3.2 refresh

阿里三面:说说你对Spring ioc启动原理的理解!

这个方法几乎完成了全部的操做,建立工厂,执行Processor等等,实例化对象,开启事件监听等等。

接下来细聊

3.3.1 prepareRefresh()

这个方法的主要做用是为应用上下文的刷新作一些准备性的工做。校验资源文件,设置启动时间和活跃状态等。

3.3.2 obtainFreshBeanFactory()

阿里三面:说说你对Spring ioc启动原理的理解!

能够get到,它主要就是建立了一个工厂BeanFactory,而且解析了配置文件,加载了Bean定义信息(面试的时候直接答这个点就够了,若是想说的能够将下面的bean信息加载聊聊)没错,标红的就是咱接下来细聊的点

阿里三面:说说你对Spring ioc启动原理的理解!

这个就是加载配置文件的过程,注意:此时仍然没有解析,解析在标红的下面

阿里三面:说说你对Spring ioc启动原理的理解!

这个就是读取的过程,具体解析流程来自parse中,这个直接调用了Java中的解析XML的类库,有兴趣自行翻阅,最后返回了一个Document对象。

经过Document对象,读取内部的标签,执行不一样的方法,逻辑和MyBatis中解析配置文件的思想相同,你们自行翻阅。

此时全部的Bean定义信息都被保存到了BeanDefinitionRegistry接口,而后走子类DefaultListableBeanFactory工厂的注册方法

阿里三面:说说你对Spring ioc启动原理的理解!

3.3.3 prepareBeanFactory(beanFactory)

为BeanFactory准备一些环境,方便在实例化的时候使用,同时添加容器本身的BeanPostProcessor

阿里三面:说说你对Spring ioc启动原理的理解!

3.3.4 postProcessBeanFactory

留给子类扩展的BeanFactoryPostProcessor,

3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

这个类,涉及到了两个接口。

  • BeanFactoryPostProcessor
  • BeanDefinitionRegistryPostProcessor接口,这个接口是BeanFactoryPostProcessor的子接口,它的优先级比BeanFactoryPostProcessor更高

它的整体执行流程是:先执行BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,而后再执行BeanFactoryPostProcessor

下图是BeanDefinitionRegistryPostProcessor接口的处理过程

阿里三面:说说你对Spring ioc启动原理的理解!

BeanFactoryPostProcessor的处理逻辑

总逻辑就是先分类,已经处理过的直接跳过,没有处理过的,分类处理,逻辑和上面的相同。

3.3.6 registerBeanPostProcessors

这个方法的逻辑和上面的同样,只不过上面是直接执行了BeanFactoryPostProcessor,而这个仅仅注册没执行。

阿里三面:说说你对Spring ioc启动原理的理解!

首先拿到工厂中全部的BeanPostProcessor类型的Bean,而后分类处理,排序注册。

3.3.7 initMessageSource()

执行国际化内容

3.3.8 initApplicationEventMulticaster

建立了一个多播器,为添加Listener提供支持。

主要逻辑:

  • 容器中是否存在applicationEventMulticaster,若是存在直接注册
  • 若是不存在,建立一个SimpleApplicationEventMulticaster,注册到容器中。

3.3.9 onRefresh()

子类扩展

3.3.10 registerListeners()

观察者模式的实现

阿里三面:说说你对Spring ioc启动原理的理解!

3.3.11 finishBeanFactoryInitialization

这一部分的内容太多了,因此采用代码和图解的方式来说解。

阿里三面:说说你对Spring ioc启动原理的理解!

下图是建立Bean的主要流程

阿里三面:说说你对Spring ioc启动原理的理解!

按照图中的序号一个一个说:

  1. BeanDefinition是否须要合并。BeanDefinition根据不一样类型的配置文件信息,会将Bean封装到不一样的Bean信息定义类中。好比咱们经常使用的配置文件版的GenericBeanDefinition;注解扫描版的ScannedGenericBeanDefinition等等。

而在这个过程当中就出现了,父定义和子定义,咱们须要在实际处理定义信息的时候进行合并处理,主要有如下三个方面

  • 存在父定义信息,使用父定义信息建立一个RootBeanDefinition,而后将自定义信息做为参数传入。
  • 不存在父定义信息,而且当前BeanDefinition是RootBeanDefintion类型的,直接返回一份RootBeanDefintion的克隆
  • 不存在父定义信息,而且当前BeanDefintion不是RootBeanDefintiton类型的,直接经过该BeanDefintion构建一个RootBeanDefintion返回

上面的流程也是源码中的执行流程

阿里三面:说说你对Spring ioc启动原理的理解!

  1. isFactoryBean。判断是否为FactoryBean

简单介绍一下:FactoryBean是让开发者建立本身须要Bean接口。内部提供了三个方法

阿里三面:说说你对Spring ioc启动原理的理解!

当咱们经过GetBean直接该Bean的时候,获取到的是该工厂指定返回的Bean类型。若是想要获取该Bean自己,须要经过一个前缀得到&

阿里三面:说说你对Spring ioc启动原理的理解!

再来看一个点,这个就是从容器中获取Bean的主要方法,也是解决循环依赖的逻辑

阿里三面:说说你对Spring ioc启动原理的理解!

来聊一下它是怎么解决循环引用的?

它引入了一个三级缓存的概念

阿里三面:说说你对Spring ioc启动原理的理解!

在发生循环引用的时候,它首先经过ObejctFactory工厂将Bean建立出来,此时的对象并无进行属性赋值,仅仅在堆中开辟了空间。而后将此时的Bean添加到earlySingletonObjects容器里,也就是说这个容器中保存的Bean都是半成品。而在以后的属性赋值中,因为对象为单例的,因此其引用地址不会发生变化,即对象最终是完整的。

1.getBean。经过这个方法直接建立了全部的对象,这也是Spring最核心的方法了,先来看一下它总体的一个流程

阿里三面:说说你对Spring ioc启动原理的理解!

它的主要逻辑是:

  • 先拿到当前要实例化的Bean的真实名字,主要是为了处理FactoryBean,拿到之后,从当前容器中看是否已经建立过该Bean,若是存在直接返回。
  • 若是不存在,获取其父工厂,若是父工厂不为空,并且当前容器中不存在当前Bean的信息,则尝试从父工厂中获取Bean定义信息,进行Bean实例化
  • 若是父工厂为空,将当前Bean信息存放到alreadyCreated缓存中。
  • 获取当前Bean的合并信息(getMergedLocalBeanDefinition),查看当前Bean是否存在依赖,若是存在则判断当前Bean和依赖 Bean 是否为循环依赖,若是不是循环依赖则先建立依赖Bean
  • 判断当前Bean的做用域。
  • 若是当前Bean是单例对象,直接建立Bean实例
  • 若是当前Bean是多例对象,将当前Bean信息添加到正在建立多例缓存中,建立完毕之后移除
  • 若是当前Bean是其余类型,如Requtst,Session等类型,则自定义一个ObejctFacotry工厂,重写getObject方法,建立对象
  • 对象建立之后,判断当前对象是否为本身须要的对象,若是是直接返回;若是不是进行类型转换,若是类型转换失败,直接抛异常

接下来看一眼CreateBean的执行

阿里三面:说说你对Spring ioc启动原理的理解!

这个方法主要完成的事情是:经过Bean的名字拿到对应的Class对象;若是当前Bean获取到的Class对象不为空且该RootDefintiton能够直接获取到该Bean,克隆一份Bean定义信息,方便以后使用。

验证当前Bean上的@Override信息。执行BeanPostProcessor,返回一个代理对象(若是存在代理的话), 若是不存在代理,则直接建立Bean

接下来咱们来聊一下这个玩意——resolveBeforeInstantiation

阿里三面:说说你对Spring ioc启动原理的理解!

来吧,继续,看一下那个前置处理器逻辑

阿里三面:说说你对Spring ioc启动原理的理解!

后置处理器就不看了,就调用了全部的后置处理器,而后执行了一遍,没有其余逻辑。

接下来继续咱们的正题:doCreateBean

阿里三面:说说你对Spring ioc启动原理的理解!

其大体流程如上图:

先判断之后是否单例,而后从FactoryBean缓存中看一下是否存在正在建立的Bean,若是存在拿出,若是不存在则建立一个当前Bean的包装类实例。而后拿到这个类的实例和实例类型,执行之后后置处理器。

当前Bean是否为单例,是否容许循环依赖,时候正在进行建立,若是是,建立一个当前Bean的ObejctFactory以解决循环依赖的问题

填充Bean的属性,进行Bean的实例化。

查看早期容器缓存中(缓存中的二级缓存中是否有该Bean)。若是有,则说明存在循环依赖,则进行处理

先看循环依赖吧

阿里三面:说说你对Spring ioc启动原理的理解!

接着来,createBeanInstance

阿里三面:说说你对Spring ioc启动原理的理解!

Spring提供了三种方式建立对象的包装:

  • 经过供给者对象对象直接建立。obtainFromSupplier
  • 经过工厂方法直接建立。
  • 默认建立。构造方法是否须要自动注入构造方法不须要自动注入,调用默认的构造方法

这个方法执行完毕之后,你应该知晓的一个点是:此时对象实例已经建立了,剩下的就是执行一系列加强器和初始化方法,属性填充等等。

咱们按照代码执行顺序来,属性填充即populateBean

这个方法执行逻辑:

  • 首先判断传入的Bean是否为null,若是为null则判断Bean定义信息中是否存在属性值,若是存在,异常;若是不存在跳过
  • 当前Bean定义信息是否为合并之后的,若是是且此时的工厂中存在InstantiationAwareBeanPostProcessors,那么在属性填充以前进行修改Bean的信息
  • 拿到全部的属性值,解析属性值的自动注入方式,Type或者Name,进行自动注入
  • 判断是否存在InstantiationAwareBeanPostProcessors,修改以前设置的属性
  • 判断是否存在依赖检查,检查依赖
  • 属性赋值

阿里三面:说说你对Spring ioc启动原理的理解!

接下来看执行初始化方法,就是调用BeanPostprocessor,init等方法

阿里三面:说说你对Spring ioc启动原理的理解!

这个就是这个方法的执行流程图,相信到这个地方,你们应该对于为何BeanPostProcessor的before方法会在init方法执行了解了。这个方法的做用仅仅是用来进行一个生命周期的打印,对象在以前已经建立了。


接下来看一下销毁的方法。registerDisposableBeanIfNecessary

对于单例Bean来讲,Spring将须要销毁的Bean存放到了disposableBeans缓存中,经过DisposableBeanAdapter封装了销毁Bean

对于其余做用域来讲,自定义了销毁回调函数,不过最后仍是封装为DisposableBeanAdapter

在封装为DisposableBeanAdapter的过程当中,会首先判断该Bean中是否存在destroy方法,而后给赋值给destroyMethodName变量。再次判断这个方法的参数,若是参数的个数大于1,则抛出异常

阿里三面:说说你对Spring ioc启动原理的理解!

3.3.12 finishRefresh

这个方法进行了一系列的资源清理和

阿里三面:说说你对Spring ioc启动原理的理解!

initLifecycleProcessor,这个方法极具简单,就看一下当前Bean中是否存在生命周期处理器,若是存在直接使用这个,若是不存在则建立一个默认的,而且注册为一个单例的扔到容器中,今日份读者福利:转发+关注 获取小编整理好的微服务全家桶学习笔记!

最后

喜欢小编今日的分享,记得关注我点赞哟,感谢支持!重要的事情说三遍,转发+转发+转发,必定要记得转发 关注哦!!!

相关文章
相关标签/搜索