又遇见到循环依赖?看完这篇再也不怕了!

569 阅读6分钟

场景:项目在升级为Dubbov2.7.14版本过程中,将DubboReference换成Autowired后,引发发循环依赖!

循环依赖其实就是循环引用(circular reference),也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

下面是我在写代码时遇到的bug,在遇到这类bug的时候我们首先检查看是否有“circular reference”这样的字样,然后再查看报错的信息中,我们明显会发现一个 “Unsatisfied dependency expressed through field xxx” , 这里的xxx代指造成循环依赖的字段,字段前面'com.leyou.auth.config.MvcConfig',表示该字段所在的这个类;因此找到这个类,在该字段上加上@Lazy进行懒加载就可以解决问题

代码中的错误日志: 关注高亮部分

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl' defined in file [C:\Users\carlos\IdeaProjects\leyou\ly-trade\target\classes\com\leyou\trade\Service\impl\OrderServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.leyou.item.client.ItemClient': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration': Unsatisfied dependency expressed through method 'setConfigurers' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.leyou.auth.config.MvcConfig': Unsatisfied dependency expressed through field 'jwtUtils'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.leyou.auth.config.AuthConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.leyou.auth.client.AuthClient': FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mvcResourceUrlProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?

但是不建议使用@Lazy

  • 因为使用时才加载,如果有错误不会在程序开始时报错,而是在运行时报错
  • 在调用相应api时,增加访问开销,降低了性能

在这个方面Spring也提供了相应的注入措施,执行流程如下: 代码片段有些枯燥,想学的看。

代码① - getSingleton
// DefaultSingletonBeanRegistry#getSingleton
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存中获取bean,即单例池
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 从二级缓存中获取对象
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 从三级缓存获取对象工厂
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
代码② - beforeSingletonCreation
// DefaultSingletonBeanRegistry#beforeSingletonCreation
protected void beforeSingletonCreation(String beanName) {
    // 1.inCreationCheckExclusions: 判断当前对象是否存在 [被剔除] 集合对象中 注解 excludeFilters=xx
    // 2.singletonsCurrentlyInCreation: 将当前对象添加 [正在创建单例Bean] 集合中
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}
代码③ - createBeanInstance
// AbstractAutowireCapableBeanFactory#createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 如果为自动装配,则推断出来各种候选的构造方法
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
        mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        // 利用推断出来的修行构造方法去实例化对象。如果使用构造器注入,这里直接返回
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        // 利用推断出来的候选构造方法去实例化对象
        return autowireConstructor(beanName, mbd, ctors, null);
    }

    // No special handling: simply use no-arg constructor.
    // 如果没有推断出合适的构造方法(或者没有提供特殊的构造方法),则使用默认的构造方法
    return instantiateBean(beanName, mbd);
}
代码④ - addSingletonFactory
// DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}
代码⑤ - populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    if (hasInstAwareBpps) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        // 这里会有三个后置处理器,分别是:
        // 		1、ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
        // 		2、AutowiredAnnotationBeanPostProcessor
        // 		3、CommonAnnotationBeanPostProcessor
        // AutowiredAnnotationBeanPostProcessor: 解析@Autowire属性注入
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            // 属性自动注入主要使用 AutowiredAnnotationBeanPostProcessor后置处理器
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                if (filteredPds == null) {
                    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                }
                pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvsToUse == null) {
                    return;
                }
            }
            pvs = pvsToUse;
        }
    }
}
代码⑥ - initializeBean
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 4-1、执行Aware (BeanNameAware、BeanClassLoaderAware 、BeanFactoryAware)
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 4.2 初始化前
        // △△ 执行Bean后置处理器(lifeCycle Callback)
        // 重写这个方法[postProcessBeforeInitialization]则会在这里被调用
        // lifeCycle callback init by annotation
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 4.3、初始化 [InitializingBean]
        // lifeCycle callback init by interface.
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }


    if (mbd == null || !mbd.isSynthetic()) {
        // 4.4、初始化后
        // 完成AOP,生成动态代理; 事件发布; 监听
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
代码⑦ - doCreateBean
// AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
if (earlySingletonExposure) {
    // 在解决循环依赖时,获取beanName从二级缓存中获取
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        }
        ...
    }
}
...
代码⑧ - getSingleton
// DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
        // 把当前对象放到 [正在创建] 的集合(singletonsCurrentlyInCreation)当中
        beforeSingletonCreation(beanName);
        
        // getObject方法会调用AbstractAutowireCapableBeanFactory的createBean方法
        singletonObject = singletonFactory.getObject();
        
        //添加到单例池
        addSingleton(beanName, singletonObject);
    }
}

文字描述如下,简单

A 生命周期

  1. class文件

  2. 通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿对象,相关代码[标记①]

  3. 把对象放到正在创建集合中(beforeSingletonCreation(beanName);) 相关代码[标记②]

  4. 通过反射实例化对象(createBeanInstance();) 相关代码[标记③]

  5. 添加对象工厂到二级缓存中 相关代码[标记④]

  6. 属性填充。在这里发现需要B对象,从单例池获取B->null->且b也没有处于正在创建集合中->实例化B且完成B的生命周期 相关代码[标记⑤]wait for b init...

  7. 初始化相关代码[标记⑥]

  8. 从二级缓存中获取Bean  相关代码[标记⑦]

  9. 添加到单例池相关代码[标记⑥]

B 生命周期

  1. class文件

  2. 通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿

  3. 把对象放到正在创建集合中(beforeSingletonCreation(beanName);)     (singletonObject = singletonFactory.getObject();)

  4. 通过反射实例化对象(createBeanInstance();)

  5. 添加对象工厂到二级缓存中

  6. 属性填充。在这里发现需要A对象,从单例池获取A->null->此时a在正在创建集合中->从三级缓存中获取对象(如果a存在切面,则返回代理对象),将对象放至二级缓存,删除三级缓存

  7. 初始化

  8. 从二级缓存中获取Bean9. 添加到单例池

如何解决

  1. 通过提前暴露自己的方式(将自己加入三级缓存)让依赖者注入,从而不会导致死循环,可以走完剩下流程。
  2. 暴露对象工厂目的是为了完成AOP代理。对象工厂清楚如何创建对象的AOP代理,但是不会立马创建,而是到合适的时机进行AOP代理对象的创建。
  3. 二级缓存存在的目的之一是保证对象只有一次AOP代理。当调用三级缓存的getObject()方法返回的对象会存入二级缓存,这样,当接下来的依赖者调用的时候, 会先判断二级缓存是否有目标对象,如果存在直接返回。

不同注入方式与循环依赖关系

依赖注入方式循环依赖能否被解决
均采用setter方法注入
均采用构造器注入不能
A中注入B的方式为setter方法,B中注入A的方式为构造器
A中注入B的方式为构造器,B中注入A的方式为setter方法不能

注: A先创建。

小结:

主对象(具备循环依赖且最先加载的类,简称A)不能通过构造函数的方法注入所依赖的Bean对象(简称B),而B不受限制,可以使用任意方式注入(setter、@Autowire、构造函数)。

循环依赖中A对象不能使用构造函数注入的原因是

在AbstractAutowireCapableBeanFactory#createBeanInstance方法中做类型推断时,如果推断是以构造方法实例B,则会直接调用AbstractAutowireCapableBeanFactory#autowireConstructor,该方法会通过调用AbstractAutowireCapableBeanFactory#doCreateBean创建对象,但是由于A对象没有提前暴露工厂(不给机会呀),所以只能一步步创建,但是走到代码DefaultSingletonBeanRegistry#getSingleton的beforeSingletonCreation方法时,由于此时A正在创建集合,强行添加则会返回false,则抛出异常。

如何不使用@Lazy,就解决循环依赖呢?

  • 延迟构造
  • 使用setter注入

总结

Spring团队建议我们使用constructor based (构造注入)的方式管理我们的依赖,对强制依赖使用断言。这里推荐使用setter注入。

Spring如何解决循环依赖?

Spring通过三级缓存解决了循环依赖。其他一级缓存为单例池,二级缓存为早期曝光对象,三级缓存为早期曝光对象工厂。当A、B两类发生循环引用,在A实例化之后,将自己提早曝光(即加入三级缓存),如果A初始AOP代理,该工厂对象返回的是被代理的对象,若未被代理,返回对象本身。当A进行属性注入时,经过之前实例化步骤,此时轮到B属性注入,调用getBean(a)获取A对象,由于A处理正在创建集合中,此时也发了循环依赖,所以可以从三级缓存获取对象工厂(如果A被AOP代理,此时返回就是代理对象),并把对象放到二级缓存中,这样保证A只经过一次AOP代理。接下来,B走完Spring生命周期流程,并放入单例池中。当B创建完后,会将B注入A,A走完Spring生命周期流程。到此,循环依赖结束。

写在最后:

循环引用说到底还是服务之间的牵扯设计不规范导致。我们得正视根本原因,做好服务的引用关系隔离。