当前位置 : 主页 > 编程语言 > java >

最易懂Spring循环依赖

来源:互联网 收集:自由互联 发布时间:2022-08-10
前言 了解Spring循环依赖和三级缓存需要熟悉IOC、AOP流程。 工作中常用new语句创建对象,通过new方法创建的对象是没有属性填充的,而Spring创建对象时可指定对象的生命周期; 说Spring的

最易懂Spring循环依赖_spring

前言

了解Spring循环依赖和三级缓存需要熟悉IOC、AOP流程。

工作中常用new语句创建对象,通过new方法创建的对象是没有属性填充的,而Spring创建对象时可指定对象的生命周期;

说Spring的bean默认是单例的是因为Spring启动时只会生成单例对象放入单例池,即singletonBeanRegistry,而使用@Scope("prototype")注解标注的类是不会加载进单例池中的。

不理解spring注入方式的同学可以先看往期这篇:[Spring Dependency Injection]

观看本篇需要熟悉getBean流程:[ 简单理解Spring getBean流程]

介绍

循环依赖就是循环引用,指两个或则两个以上的bean互相依赖对方,最终形成闭环。比如“A对象依赖B对象,而B对象也依赖A对象”。

SpringIOC中分为两种情况:

  • 普通的Bean死循环创建
  • 创建的Bean被AOP代理
  • Spring解决循环依赖的前提:

  • 只支持单例对象。原型prototype的场景如果循环依赖就会造成无限套娃,所以是不被支持的,spring会抛出异常。 我们谈的循环依赖都是依据单例对象相互依赖产生的循环依赖问题。
  • 不支持构造构造器注入的循环依赖(这里说的是A、B对象均采用构造方法注入)。
  • 熟悉依赖注入方式的应该清楚,构造器注入是通过构造方法来生成对象,其必须要先获取属性,才能生成调用构造方法进行实例化,这种情况的循环依赖是无法解决的。

    属性注入和setter注入时是需要使用autowried的,而构造器注入是不需要的。

    熟悉autowried底层应该清楚,autowired是在实例化后、初始化完成前调用后置处理器从IOC容器中拿到bean通过反射来为对象属性赋值的。

    网上找了一张图来说明A、B对象依赖注入方式不同时spring能否解决循环依赖。

    最易懂Spring循环依赖_构造器_02

    非aop循环依赖

    最易懂Spring循环依赖_构造器_03

    通过调用栈可以看出解决循环依赖的关键是在A对象填充属性前将一个匿名内部类ObjectFactory对象放在SingletonFactory(三级缓存)中,在有依赖对象A的对象B填充属性A时通过getSingleton方法的ObjectFactory.getObject() 直接调用内部类中专门用来获取A半成品对象的getEarlyBeanReference方法,从而完成填充属性。

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
    }
    }
    }
    return exposedObject;
    }

    getEarlyBeanReference方法则会循环调用bean后置处理器的getEarlyBeanReference方法,非aop情况下,会调用两个后置处理器,其都是直接返回传参的bean,未做处理。

    最易懂Spring循环依赖_构造器_04

    aop循环依赖

    此处不讲解AOP如何生成代理对象,在下篇文章会进行讲解

    在aop代理的情况下,则getEarlyBeanReference方法中会多出一个后置处理器;

    最易懂Spring循环依赖_sed_05

    在调用bean后置处理器的getEarlyBeanReference方法中,会通过wrapIfNecessary生成代理对象。

    // Aop提前引用入口
    public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
    }

    熟悉AOP的应该清楚,aop代理对象是在初始化那一步的后置处理器的postProcessAfterInitialization方法中生成代理对象的,同样是wrapIfNecessary方法,和上面的getEarlyBeanReference方法都出自AbstractAutoProxyCreator类。

    //AOP 创建代理对象入口
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    //如果没有被提前代理过
    if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    //最终会调用createProxy,创建代理bean
    return wrapIfNecessary(bean, beanName, cacheKey);
    }
    }
    return bean;
    }
    上一篇:连接数据库实现查询员工信息
    下一篇:没有了
    网友评论