Spring注解-4.IOC原理与循环依赖

目录

一、BeanDefinition加载解析及注册(创建BeanFactory)

二、BeanFactory的预准备/处理

三、注册并执行BeanFactory的后置处理器

四、注册Bean的后置处理器

五、初始化/注册其他组件

六、初始化所有剩下的单实例bean(重头戏)

七、完成IOC容器的刷新

八、(附加)循环依赖/三级缓存

8.1 什么是循环依赖? 

8.2循环依赖什么情况下可以被解决?

8.3 循环依赖如何解决?

8.3.1 无AOP

8.3.2 有AOP(三级缓存的奥秘之处)

8.4 总结

8.4.1 ”Spring是如何解决的循环依赖?“

8.4.2 ”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“


        IoC容器是Spring的核⼼模块,是抽象了对象管理依赖关系管理的框架解决⽅案。Spring 提供了很多 的容器,其中 BeanFactory 顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器必须遵从的⼀套原则,具体的容器实现可以增加额外的功能,⽐如我们常⽤到的ApplicationContext,其下更具体的实现如 ClassPathXmlApplicationContext 包含了解析 xml 等⼀系列的内容, AnnotationConfigApplicationContext 则是包含了注解解析等⼀系列的内容。

Spring注解-4.IOC原理与循环依赖_第1张图片

        ApplicationContext 除了继承BeanFactory⼦接⼝, 还继承了ResourceLoaderMessageSource等接⼝,因此其提供的功能也就更丰富了。

        通过断点调试发现,IOC容器的创建过程基本都是在AbstractApplicationContext refresh()方法中实现的:

 public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            //1.预处理
            this.prepareRefresh();
            //2.获取BeanFactory。
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            //3.BeanFactory的预准备工作
            this.prepareBeanFactory(beanFactory);

            try {
                //4.BeanFactory准备⼯作完成后进⾏的后置处理⼯作(在BeanFactory准备工作完成后做一些定制化的处理,没有具体实现)
                this.postProcessBeanFactory(beanFactory);
                //5.实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
                this.invokeBeanFactoryPostProcessors(beanFactory);
                //6.注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后执⾏
                this.registerBeanPostProcessors(beanFactory);
                //7.初始化MessageSource组件
                this.initMessageSource();
                //8.初始化事件派发器
                this.initApplicationEventMulticaster();
                //9.留给子容器(子类),在容器刷新的时候可以⾃定义逻辑
                this.onRefresh();
                //10.注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
                this.registerListeners();
                //11.初始化所有剩下的⾮懒加载的单例bean
                //  初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)
                //  填充属性
                //  初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)
                //  调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处理
                this.finishBeanFactoryInitialization(beanFactory);
                //12.完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件 (ContextRefreshedEvent)。IOC容器创建完成
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

        其中,Bean对象创建的⼏个关键时机点

Spring注解-4.IOC原理与循环依赖_第2张图片

        下面给出了大致的流程图,其中最重要的是Bean的生命周期部分,这里额外给一个图: 

Spring注解-4.IOC原理与循环依赖_第3张图片

Spring注解-4.IOC原理与循环依赖_第4张图片

                 下面再细说。

一、BeanDefinition加载解析及注册(创建BeanFactory)

        该阶段的目标是创建BeanFactory(DefaultListableBeanFactory,将配置文件中的信息进行定位、解析、加载为BeanDefinition并保存在BeanFactory对象中。

的过程

Spring注解-4.IOC原理与循环依赖_第5张图片

        该流程涉及到几个关键点:

        1.Resource定位:指对BeanDefinition的资源定位过程。通俗讲就是找到定义Javabean信息的XML⽂ 件,并将其封装成Resource对象。

        2.BeanDefinition载⼊ :把⽤户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。

        3.注册BeanDefinition到 IoC 容器

         该过程在整体流程图第2步完成(obtainFreshBeanFactory)。这是第2步的简略时序图

Spring注解-4.IOC原理与循环依赖_第6张图片

 BeanDefinition 涉及到的部分方法:

        调用refreshBeanFactory -> loadBeanDefinitions ->AbstractBeanDefinitionReader->子实现类的doLoadBeanDefinitions -> doLoadBeanDefinitions -> registerBeanDefinitions。细节不再深入。

        该过程的时序图:

Spring注解-4.IOC原理与循环依赖_第7张图片

        所谓的注册就是把封装的 XML 中定义的 Bean信息封装为 BeanDefinition 对象之后放⼊⼀个Map中,BeanFactory 是以 Map 的结构组织这些 BeanDefinition 的。

二、BeanFactory的预准备/处理

Spring注解-4.IOC原理与循环依赖_第8张图片

        该阶段完成BeanFactory预处理工作(对应第3步-prepareBeanFactory

        例如:

1)设置BeanFactory的类加载器支持表达式解析器...
2)添加部分BeanPostProcessorApplicationContextAwareProcessor】【ApplicationListenerDetector
3)设置忽略自动装配的接口EnvironmentAwareEmbeddedValueResolverAware
4)注册可以解析的自动装配;我们能直接在任何组件中自动注入:
      BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5)添加编译时的AspectJ
6)给BeanFactory中注册一些能用的组件;
           environment【ConfigurableEnvironment】、
           systemProperties【Map】、
           systemEnvironment【Map

        第4步postProcessBeanFactory可以在BeanFactory准备工作完成后进行一些定制的后置处理工作,默认没有实现,子类通过重写这个方法来在BeanFactory创建并预准备完成后做进一步的设置。

三、注册并执行BeanFactory的后置处理器

        该阶段实例化调用实现了BeanFactoryPostProcessor以及BeanDefinitionRegistryPostProcessorBean后置处理器可以有多个。(对应第5步-invokeBeanFactoryPostProcessors

        注意:

        这些后置处理器可以实现PriorityOrdered-优先级接口/Ordered-顺序接口,执行时是按照优先级顺序执行的。 (PriorityOrdered  -> Ordered -> 无

Spring注解-4.IOC原理与循环依赖_第9张图片

        这里先执行BeanDefinitionRegistryPostProcessor再执行BeanFactoryPostProcessor

        BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor子接口,二者都是后置处理器

        BeanDefinitionRegistryPostProcessor 侧重于创建自定义的bd,而BeanFactoryPostProcessor侧重于对已有bd属性的修改

四、注册Bean的后置处理器

Spring注解-4.IOC原理与循环依赖_第10张图片

         该阶段注册BeanPostProcessor(Bean的后置处理器)

        不同接口类型的BeanPostProcessor在Bean创建前后的执行时机是不一样的。(对应第6步-registerBeanPostProcessors

        例如:

BeanPostProcessor、
DestructionAwareBeanPostProcessor、
InstantiationAwareBeanPostProcessor、
SmartInstantiationAwareBeanPostProcessor、
         

  注意:

        这些后置处理器可以实现PriorityOrdered-优先级接口/Ordered-顺序接口,执行时是按照优先级顺序执行的。 (PriorityOrdered  -> Ordered -> 无

        最后还会注册一个ApplicationListenerDetector

        该BeanPostProcessor检测那些实现了ApplicationListener接口的bean,在它们创建初始化之后将它们添加到ApplicationContext事件多播器上;并在这些ApplicationListener bean销毁之前将它们从ApplicationContext事件多播器移除。 

五、初始化/注册其他组件

Spring注解-4.IOC原理与循环依赖_第11张图片

         这里对应第7、8、9、10步

        initMessageSource();初始化MessageSource组件(做国际化功能消息绑定,消息解析

        initApplicationEventMulticaster();初始化事件派发器

1)从BeanFactory中获取ApplicationEventMulticaster

3)如果没有配置就创建一个SimpleApplicationEventMulticaster 添加到BeanFactory

         onRefresh();留给子容器(子类)子类重写这个方法,在容器刷新的时候可以自定义逻辑

         registerListeners();给容器中将所有项目里面的ApplicationListener注册进来;

1、从容器中拿到所有的ApplicationListener

2、将每个监听器添加到事件派发器

3、派发之前步骤产生的事件

六、初始化所有剩下的单实例bean(重头戏)

        这里对应第11步(finishBeanFactoryInitialization

Spring注解-4.IOC原理与循环依赖_第12张图片

        该方法会实例化所有剩余的非懒加载单例 bean。除了一些内部 bean、实现了 BeanFactoryPostProcessor 接口的 bean、实现了 BeanPostProcessor 接口的 bean,其他的非懒加载单例 bean 都会在这个方法中被实例化,并且 BeanPostProcessor 触发也是在这个方法中。 

        主要用到的就是 beanFactory.preInstantiateSingletons() 方法,其主要任务是进行初始化,首先获取Bean的定义信息-RootBeanDefinition,然后是一系列判断,如是否是懒加载的,是否是单实例的,是否是factorybean,最后调用getBean()->dogetBean()方法。

        dogetBean()的大致流程:

Spring注解-4.IOC原理与循环依赖_第13张图片

1. 转换对应 beanName

        这里传入的参数可能是别名,也可能是 FactoryBean,所以需要进行一系列的解析。

2.尝试从缓存中加载单例

        主要目的就是为了解决单例模式下的循环依赖

3.Bean 的实例化
        返回对应实例,这里的作用就是判断对应的Bean 是不是FactoryBean类型,如果是 就通过调用对应的getObject() 方法,返回对应的实例,如果不是FactoryBean类型,直接返回 

4.原型模型的依赖检查
        只有在单例情况下才会尝试解决循环依赖,原型模式下如果存在循环依赖就直接报错

5.检查parentBeanFactory

        递归的调用父工厂getBean 方法

6.根据beanName获取合并过的对应的RootBeanDefinition

        所有的bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性

7.寻找依赖
        判断是否存在依赖,如果存在依赖,那就先创建对应的依赖的Bean
8.针对不同的 scope 进行 bean 的创建
        根据不同的配置进行不同的初始化策略。(回到3)
9.类型检验并转换

        具体的实例化过程createBean()方法中,我们看createBean()->docreateBean()方法。

Spring注解-4.IOC原理与循环依赖_第14张图片

        1.判断是否是单例模式,若是就从缓存 factoryBeanInstanceCache 获取,并将缓存清除
        2.如果不是单例模式,或者缓存没有对应的value,就开始创建实例,调用createBeanInstance 方法(利用工厂方法或者对象的构造器创建出Bean实例)
        3.修改合并之后的 BeanDefinition,bean 合并后的处理Autowired 注解正是通过此方法实现诸如类型的预解析
        4.依赖处理 ,这里有一个放入缓存,以便其他的依赖此bean的 bean可以提前获取到, 这里调用了getEarlyBeanReference 进行提前曝光
        5.属性填充, 调用了 populateBean方法,将各个属性值注入
        6.初始化Bean ,这里对各种Aware 设置、前置后置 等相关的处理- initialzingBean
        7.循环检查,检查是否还有依赖bean没有创建
        8.注册disposableBeans 里面,以便在销毁bean 的时候 后置处理也生效

         关于populateBean:

1)拿到InstantiationAwareBeanPostProcessor后置处理器;
2)注入Bean属性

         关于initializeBean

1)【执行Aware接口方法】invokeAwareMethods(beanName, bean);
2)【执行后置处理器-初始化之前】        
        applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
3)【执行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
   1)是否是InitializingBean接口的实现;执行接口规定的初始化;
   2)是否自定义初始化方法;
4)、【执行后置处理器-初始化之后
        applyBeanPostProcessorsAfterInitialization

         这里就对应了该流程的第一个图。

七、完成IOC容器的刷新

        1)initLifecycleProcessor() - 初始化和生命周期有关的后置处理器
        2)拿到前面定义的生命周期后置处理器(BeanFactory),回调onRefresh();
        3)publishEvent(new ContextRefreshedEvent(this)) - 发布容器刷新完成事件

八、(附加)循环依赖/三级缓存

        我们首先要了解Spring创建IOC容器过程中用到的三种缓存:

  1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean

  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象

  3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

8.1 什么是循环依赖? 

Spring注解-4.IOC原理与循环依赖_第15张图片

        循环依赖的意思就是A依赖了BB又依赖A。依赖方式有构造器参数注入setter属性注入

8.2循环依赖什么情况下可以被解决?

        A、B相互依赖的方式都是构造器注入时,不能解决

        A、B不是单例不能解决

        A、B 不都是用构造器注入,有可能解决

        A、B 都是Setter注入,可以解决

Spring注解-4.IOC原理与循环依赖_第16张图片

         为什么第三种情况是有可能解决,而不是一定呢?

        因为Spring在创建Bean时默认会根据自然排序进行创建,所以A会先于B进行创建

        A使用构造器属性注入B时,依赖属性B的getBean调用提前了,这时A还没有放入三级缓存。此时依赖属性B进行实例化后想使用setter注入属性A,或者用构造器注入参数A都是不可能的了!因为A在缓存里找不到!!

        假如A注入B的方式是setter,则A在注入属性B之前就已经将自己放到缓存中了,那B即无论使用构造器注入A,还是setter注入A都是可行的,因为B在缓存里能找到A。

8.3 循环依赖如何解决?

8.3.1 无AOP

        首先,我们要知道Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A。Spring在创建Bean的过程中分为三步

  1. 实例化,对应方法:createBeanInstance(简单理解就是new了一个对象

  2. 属性注入,对应方法:populateBean(为实例化中new出来的对象填充属性

  3. 初始化,对应方法:initializeBean(执行aware接口的方法,初始化方法,AOP代理

Spring注解-4.IOC原理与循环依赖_第17张图片

        在完成Bean的实例化后,属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中。这里只是添加了一个工厂,通过这个工厂(ObjectFactory)的getObject方法可以得到一个对象

        当A完成了实例化并添加进了三级缓存后,就要开始为A进行属性注入了,在注入时发现A依赖了B,那么这个时候Spring又会去getBean(b),然后反射调用setter方法完成属性注入。

        这时B进行实例化,然后注入属性A,因为A对应的工厂已经存在了三级缓存中,所以直接从缓存中取出A放进二级缓存,并移除三级缓存中A的工厂。B初始化完成。

        最后再回到A属性注入的流程中,成功注入B的属性,再初始化

        初始化完成的Bean最终都会放入一级缓存中。

        这种情况下这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP的情况下三级缓存没什么用

8.3.2 有AOP(三级缓存的奥秘之处)

        从三级缓存获取对象会调用AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference方法。

        如果对A进行了AOP代理,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建的对象

Spring注解-4.IOC原理与循环依赖_第18张图片

         在给B注入的时候为什么要注入一个代理对象?

        答:当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象(A的代理对象替换原来的A对象)

        三级缓存的作用在用处?

        答:这个工厂的目的在于延迟实例化阶段生成的对象代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象

        对于A,无论其是否有循环依赖,只要它被AOP代理,就一定会存在三级缓存中来提前暴露一个工厂对象。这时还不知道它是否存在循环依赖(还没注入属性呢)

        如果直接放入二级缓存,那现在就要为A创建代理(此时未初始化)。这违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过一个后置处理器来完成的,在这个后置处理器中对初始化后的Bean完成AOP代理。

        如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步(初始化)完成代理而不是在实例化后就立马完成代理

        这是使用三级缓存时的情况: 

Spring注解-4.IOC原理与循环依赖_第19张图片

         这是不使用三级缓存,直接放在二级缓存时的情况:

Spring注解-4.IOC原理与循环依赖_第20张图片

        上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。 

8.4 总结

8.4.1 ”Spring是如何解决的循环依赖?“

        答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂singletonFactories)。

        当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么后面通过这个工厂获取到的就是A代理后的对象;如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象

        当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取。第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

8.4.2 ”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“

        答:如果要使用二级缓存解决循环依赖,意味着所有Bean实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是实例化后就立马进行AOP代理。

        注:三级缓存并不提高效率,只是为了尽可能不违背Spring的Bean生命周期的原则

你可能感兴趣的:(Spring5,java,后端,spring,1024程序员节)