具体看这篇博客–>> Spring Bean 的生命周期
被 Spring 管理的对象叫做 Bean 。Bean的生成步骤如下:
1、Spring 扫描 class 得到 BeanDefinition;
2、根据得到的 BeanDefinition 去生成 bean;
3、首先根据 class 推断构造方法;
4、根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象);
5、填充原始对象中的属性(依赖注入);
6、如果原始对象中的某个方法被 AOP 了,那么则需要根据原始对象生成一个代理对象;
7、把最终生成的代理对象放入单例池(源码中叫做 singletonObjects)中,下次 getBean 时就直接从单例池拿即可;
8、对于 Spring 中的 Bean 的生成过程,步骤还是很多的,并且不仅仅只有上面的7步,还有很多很多,这里不详细说了。
我们可以发现,在得到一个原始对象后,Spring 需要给对象中的属性进行依赖注入,那么这个注入过程是怎样的?
比如上文说的 A 类,A 类中存在一个 B 类的 b 属性,所以,当 A 类生成了一个原始对象之后,就会去给 b 属性去赋值,此时就会根据 b 属性的类型和属性名去 BeanFactory 中去获取 B 类所对应的单例bean。
1. 如果此时 BeanFactory 中存在 B 对应的 Bean,那么直接拿来赋值给 b 属性;
2. 如果此时 BeanFactory 中不存在 B 对应的 Bean,则需要生成一个 B 对应的 Bean,然后赋值给 b属性。
问题就出现在「第二种」情况,如果此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期。
那么在创建 B 类的 Bean 的过程中,如果 B 类中存在一个 A 类的 a 属性,那么在创建 B 的 Bean 的过程中就需要 A 类对应的 Bean,但是,触发 B 类 Bean 的创建的条件是 A 类 Bean 在创建过程中的依赖注入,所以这里就出现了循环依赖:
A Bean创建–>依赖了 B 属性–>触发 B Bean创建—>B 依赖了 A 属性—>需要 A Bean(但A Bean还在创建过程中)
从而导致 A Bean 创建不出来,B Bean 也创建不出来。
这是循环依赖的场景,但是上文说了,在 Spring 中,通过某些机制帮开发者解决了部分循环依赖的问题,这个机制就是「三级缓存」。
一级缓存为:singletonObjects;
二级缓存为:earlySingletonObjects;
三级缓存为:singletonFactories;
/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
singletonObjects」中缓存的是已经经历了完整生命周期的bean对象。
「earlySingletonObjects」比 singletonObjects 多了一个 early ,表示缓存的是早期的 bean对象。早期指的是 Bean 的生命周期还没走完就把这个 Bean 放入了 earlySingletonObjects。
「singletonFactories」中缓存的是 ObjectFactory,表示对象工厂,用来创建某个对象的。
refSpring中的循环依赖_spring循环依赖_SuZhan7710的博客-CSDN博客
循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环。
我们这里以两个类A和B为例进行讲解,如下是A和B的声明:
@Component
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
结论先行:
1.构造器循环依赖----初始化失败
2.field属性注入并且是单列模式循环依赖(setter方式)----初始化成功
3.field属性注入循环依赖(prototype)----初始化失败
现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。
Spring bean注入流程
类实例化 -> 属性注入 -> 执行初始化方法 -> (如果有需要)生成代理对象 -> 使用
三级缓存解决循环依赖流程:
A、B两个类相互依赖,初始化A的时候,第一步实例化A完成(生成对象工厂实例放入三级缓存),注入依赖属性B,一级缓存查询B没有,二级缓存查询B没有,
初始化B(生成对象工厂实例放入三级缓存),注入依赖属性A,一级缓存查询A没有,二级缓存查询A没有,三级缓存查询到A的对象工厂,需要AOP增强则生成A的代理对象,没有则直接创建A实例对象,并将A放入到二级缓存,注入A的代理对象完成,生成代理对象B,B移入一级缓存。
继续A属性注入(B的代理对象),然后可能还会依赖注入C对象流程和B一致,所有依赖注入完成后A初始化,生成A的代理对象,发现A的代理对象已存在,则跳过,放入一级缓存。此时A的代理对象也是提前生成的,但是仅针对循环依赖提前生成。
下面为流程图:
三级缓存不存在循环依赖的正常生成流程:
第一步实例化A完成(生成对象工厂实例放入三级缓存,此时虽然放入三级缓存,但没有生成代理对象),注入属性,执行初始化方法,生成代理对象,移入一级缓存。
ref spring为什么不能只用一二级缓存来解决循环依赖? - sahara-随笔 - 博客园
spring中出现循环依赖主要有以下场景:
1.重新设计
当有一个循环依赖,很可能是有一个设计问题并且各责任没有得到很好的分离。应该尽量正确地重新设计组件,以便它们的层次是精心设计的,也没有必要循环依赖。
如果不能重新设计组件(可能有很多的原因:遗留代码,已经被测试并不能修改代码,没有足够的时间或资源来完全重新设计…),但有一些变通方法来解决这个问题,如下面几种方式
2.延迟加载@Lazy注解:
原理是发现有@lazy注解的依赖为其生成代理类,依赖代理类,从而实现了解耦
解决Spring 循环依赖的一个简单方法就是对一个Bean使用延时加载。也就是说:这个Bean并没有完全的初始化完,实际上他注入的是一个代理,只有当他首次被使用的时候才会被完全的初始化。
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
public ServiceAImpl(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
3.Setter/Field 注入
简单地说,你对你需要注入的bean是使用setter注入(或字段注入),而不是构造函数注入。
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
已经完成了bean的所有流程,即实例化、注入、初始化完成的bean实例,并放进了单列池中了。
一级缓存:
/** 保存所有的singletonBean的实例,已经完成了所有流程可以用的bean */
private final Map singletonObjects = new ConcurrentHashMap(64);
保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入,但已经提前完成了AOP增强。
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map earlySingletonObjects = new HashMap(16);
为什么需要二级缓存
如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象,而三级缓存中的取出来的动态代理对象每次都是新对象,地址值不一样。
singletonBean的生产工厂,即创建单列bean的工厂。
存的是一个函数接口, 函数接口实现 创建动态代理调用BeanPostProcessor 。 为了避免重复创建, 调用把返回的动态代理对象或者原实例存储在二级缓存中。
/** singletonBean的生产工厂,即创建单列bean的工厂*/
private final Map> singletonFactories = new HashMap>(16);
在取三级缓存并创建bean时,ObjectFactory调用getObject(),不仅会取到bean实例而且还会判断是否生成代理对象,并放到二级缓存中。
为什么需要第三级缓存?
解决如果出现循环依赖,判断是否需要提前进行aop等操作
一级缓存:
/** 保存所有的singletonBean的实例,已经完成了所有流程可以用的bean */
private final Map singletonObjects = new ConcurrentHashMap(64);
二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map earlySingletonObjects = new HashMap(16);
三级缓存:
/** singletonBean的生产工厂,即创建单列bean的工厂*/
private final Map> singletonFactories = new HashMap>(16);
/** 保存所有已经完成初始化的Bean的名字(name) */
private final Set registeredSingletons = new LinkedHashSet(64);
/**
标识指定name的Bean对象是否处于创建状态,这个状态非常重要。
如果创建完毕之后,会将其从 singletonsCurrentlyInCreation 列表中移除,
并且会将创建好的 bean 放到另外一个单例列表中,这个列表叫做 singletonObjects即一级缓存中。
*/
private final Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap(16));
ref Spring 循环依赖解决方案_spring如何解决循环依赖-CSDN博客