场景:项目在升级为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 生命周期
-
class文件
-
通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿对象,相关代码[标记①]
-
把对象放到正在创建集合中(beforeSingletonCreation(beanName);) 相关代码[标记②]
-
通过反射实例化对象(createBeanInstance();) 相关代码[标记③]
-
添加对象工厂到二级缓存中 相关代码[标记④]
-
属性填充。在这里发现需要B对象,从单例池获取B->null->且b也没有处于正在创建集合中->实例化B且完成B的生命周期 相关代码[标记⑤]wait for b init...
-
初始化相关代码[标记⑥]
-
从二级缓存中获取Bean 相关代码[标记⑦]
-
添加到单例池相关代码[标记⑥]
B 生命周期
-
class文件
-
通过beanName去缓存中获取对象。一开始,单例池没有,且a也没有处于正在创建集合中,所以不会去二级缓存、三级缓存拿
-
把对象放到正在创建集合中(beforeSingletonCreation(beanName);) (singletonObject = singletonFactory.getObject();)
-
通过反射实例化对象(createBeanInstance();)
-
添加对象工厂到二级缓存中
-
属性填充。在这里发现需要A对象,从单例池获取A->null->此时a在正在创建集合中->从三级缓存中获取对象(如果a存在切面,则返回代理对象),将对象放至二级缓存,删除三级缓存
-
初始化
-
从二级缓存中获取Bean9. 添加到单例池
如何解决
- 通过提前暴露自己的方式(将自己加入三级缓存)让依赖者注入,从而不会导致死循环,可以走完剩下流程。
- 暴露对象工厂目的是为了完成AOP代理。对象工厂清楚如何创建对象的AOP代理,但是不会立马创建,而是到合适的时机进行AOP代理对象的创建。
- 二级缓存存在的目的之一是保证对象只有一次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生命周期流程。到此,循环依赖结束。
写在最后:
循环引用说到底还是服务之间的牵扯设计不规范导致。我们得正视根本原因,做好服务的引用关系隔离。