• 一文带你深入了解IOC启动原理


    1. IOC概述

    1.1 是什么?

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

    来看一下传统的干活方式:在对象单一职责原则的基础上,一个对象很少有不依赖其他对象而完成自己的工作,所以这个时候就会出现对象之间的依赖。而体现在我们的开发中,就是需要什么对象的时候,就创建什么对象,此时对象创建的控制权在我们自己手里。当对象创建的太多的时候,就会出现一个对象更改,就得更改所有依赖它的对象,耦合性大。自主性体现的同时也出现了对象耦合严重的情况

    • 这个时候,我们就会思考,能不能我们在用的时候直接拿到这个对象去用,而将创建对象的能力交给第三方,这样我们就不需要关心对象是怎么创建的了。即将自己的控制权交出去。这就是控制反转
    • 这个时候,就会有另一个问题产生了,对象怎么才能直接被我们拿来用呢。对象创建的时候,我们把这个对象注入到这个对象中,然后就可以使用了。这就是依赖注入
    • 另一个问题,耦合性怎么被解决掉的?通过控制反转我们仅仅使用了这个对象,如果对象发生了修改,我们仅仅需要修改第三方创建对象的方式即可,这个时候难道还会出现所谓的对象耦合吗?
    • Spring全家桶地址:Spring最新全家桶资料。

    2. IOC架构

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

    我们接下来一步一步来解读。

    2.1 白话版

    在第一章中我们了解了IOC是来帮助我们管理和创建对象的。

    这个时候我们需要一个承载我们需要创建信息的容器,即图中的XML或者注解,那么有了我们自己的BeanDefiniton信息以后,我们需要一个接口用来读取这些信息,于是出现了BeanDefinitionReader用来读取我们自己的Bean信息。

    那么我们需要考虑一个问题了,那么多的对象怎么生产呢?

    答案就是工厂模式。Spring默认的工厂是DefaultListableBeanFactory,没错,Spring中的所有对象(容器对象和我们自己创建的对象)都是由他创建的。大批量生产对象

    这个时候又有了一个问题,我们不想通过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容器加载和实例化对象的过程。第三章的代码解读中我们仔细看

    执行过程:

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

    3. IOC源码解读

    3.1 上下文配置启动

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

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

    3.2 refresh

    这个方法几乎完成了所有的操作,创建工厂,执行Processor等等,实例化对象,开启事件监听等等。

    接下来细聊

    3.3.1 prepareRefresh()

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

    3.3.2 obtainFreshBeanFactory()

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

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

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

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

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

    3.3.3 prepareBeanFactory(beanFactory)

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

    3.3.4 postProcessBeanFactory

    留给子类扩展的BeanFactoryPostProcessor,

    3.3.5 invokeBeanFactoryPostProcessors(beanFactory)

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

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

    它的总体执行流程是:先执行BeanDefinitionRegistryPostProcessor的BeanFactoryPostProcessor,然后再执行BeanFactoryPostProcessor

    下图是BeanDefinitionRegistryPostProcessor接口的处理过程

    BeanFactoryPostProcessor的处理逻辑

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

    3.3.6 registerBeanPostProcessors

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

    首先拿到工厂中所有的BeanPostProcessor类型的Bean,然后分类处理,排序注册。

    3.3.7 initMessageSource()

    执行国际化内容

    3.3.8 initApplicationEventMulticaster

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

    主要逻辑:

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

    3.3.9 onRefresh()

    子类扩展

    3.3.10 registerListeners()

    观察者模式的实现

    3.3.11 finishBeanFactoryInitialization

    这一部分的内容太多了,所以采用代码和图解的方式来讲解。

    下图是创建Bean的主要流程

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

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

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

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

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

    1. isFactoryBean。判断是否为FactoryBean

    简单介绍一下:FactoryBean是让开发者创建自己需要Bean接口。内部提供了三个方法

    当我们通过GetBean直接该Bean的时候,获取到的是该工厂指定返回的Bean类型。如果想要获取该Bean本身,需要通过一个前缀获得&

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

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

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

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

    1.getBean。通过这个方法直接创建了所有的对象,这也是Spring最核心的方法了,先来看一下它整体的一个流程

    它的主要逻辑是:

    • 先拿到当前要实例化的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的执行

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

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

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

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

    后置处理器就不看了,就调用了所有的后置处理器,然后执行了一遍,没有其他逻辑。

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

    其大致流程如上图:

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

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

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

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

    先看循环依赖吧

    接着来,createBeanInstance

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

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

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

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

    这个方法执行逻辑:

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

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

    这个就是这个方法的执行流程图,相信到这个地方,大家应该对于为什么BeanPostProcessor的before方法会在init方法执行了解了。这个方法的作用仅仅是用来进行一个生命周期的打印,对象在之前已经创建了。


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

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

    对于其他作用域来说,自定义了销毁回调函数,不过最后还是封装为DisposableBeanAdapter

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

    3.3.12 finishRefresh

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

    initLifecycleProcessor,这个方法极具简单,就看一下当前Bean中是否存在生命周期处理器,如果存在直接使用这个,如果不存在则创建一个默认的,并且注册为一个单例的扔到容器中。

  • 相关阅读:
    ASE19团队项目 beta阶段 model组 scrum report list
    ASE19团队项目 beta阶段 model组 scrum7 记录
    ASE19团队项目 beta阶段 model组 scrum6 记录
    ASE19团队项目 beta阶段 model组 scrum5 记录
    ASE19团队项目 beta阶段 model组 scrum4 记录
    ASE19团队项目 beta阶段 model组 scrum3 记录
    ASE19团队项目 beta阶段 model组 scrum2 记录
    ASE19团队项目 beta阶段 model组 scrum1 记录
    【ASE模型组】Hint::neural 模型与case study
    【ASE高级软件工程】第二次结对作业
  • 原文地址:https://www.cnblogs.com/QLCZ/p/14976740.html
Copyright © 2020-2023  润新知