问题提出
Spring中代码-产生循环依赖问题
@Component
class A{
private B b;
public A(B b) {
this.b = b;
}
}
@Component
class B{
private A a;
public B(A a) {
this.a = a;
}
}
报错
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-05-26 12:46:05.173 ERROR 10804 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| a defined in file [C:\Users\lianlei\IdeaProjects\videowebsite\target\classes\com\ll\videowebsite\utils\A.class]
↑ ↓
| b defined in file [C:\Users\lianlei\IdeaProjects\videowebsite\target\classes\com\ll\videowebsite\utils\B.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
问题:
The dependencies of some of the beans in the application context form a cycle:
循环依赖注入,主要是因为,使用构造方法进行注入,A对象初始化的时候需要用到IOC容器当中的B对象,而B对象同样需要IOC中的A对象,因此报错。但是如果是通过Setter方法注入,就不会产生该问题
原因如下:
在第一步和第二内部,是首先创建实例化对象,然后进行属性注入,如果采用构造方法注入,A、B两个对象都无法成功实例化,但是如果使用的是setter方法,就可以在实例化之后进行属性注入,就不会有问题。如果构造方法和setter方法同时存在,会优先使用构造方法,因此会爆错。
解决方法
1. 懒加载
在其中一个需要加载的地方加上 @Lazy 注解,就可以延迟加载该属性。
@Component
@Component
class A{
private B b;
@Lazy
public A(B b) {
this.b = b;
}
}
如上,在private B b;加上 @Lazy ,再初始化A对象的时候,就不会直接注入a,不用去IOC容器当中查找单例对象A,就不会产生循环依赖问题。创建B的时候依然会使用A的构造方法,但是由于使用@Lazy注解,因此Spring会生成一个代理对象返回,使得A可以正常创建。从而解决以来循环问题。
使用@Lazy解决循环依赖问题
2. 二级缓存
在缓存池当中缓存A和B对象,其中属性都是空白的。
缺点:
如果注入的是被AOP管理的对象,那么就需要在单例池当中注入被动态代理的对象,而不是原对象。
怎么办?
三级缓存
三级缓存:如果引用的对象配置了AOP,那在单例池中最终就会需要注入动态代理对象,而不是原对象。而生成动态代理是要在对象初始化完成之后才开始的。于是Spring增加三级缓存,保存所有对象的动态代理配置信息。在发现有循环依赖时,将这个对象的动态代理信息获取出来,提前进行AOP,生成动态代理。
/*一级缓存,理解为:已经实例化,属性填充,初始化完成的 bean*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/*三级缓存*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/*二级缓存,理解为:实例化完成但属性填充未完成的 bean*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从一级缓存,也就是单例池当中获取数据
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//一级缓存没有就从二级缓存拆线呢
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized(this.singletonObjects) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//二级也没有,说明是需要AOP,因此会从三级缓存加载singletonFactory ,这里面保存的的代理信息,通过代理信息,在二级缓存当中生成代理对象,从而可以解决依赖循环。 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
注意:
如果不是单例模式,就不会出现循环引用的问题。