所谓的循环依赖是指,A依赖B,B又依赖A,它们之间形成了相互依赖的关系。或者A依赖B,B依赖C,C又依赖A,这样就形成了一个闭环。它们之间的依赖关系是:
2、通过手写代码来理解Spring循环依赖:
1)A类中注入了B
@Component
public class InstanceA {
@Autowired
private InstanceB instanceB;
public InstanceB getInstanceB() {
return instanceB;
}
public void sayHello(){
System.out.println("say hello!");
}
}
2)B类中依赖A
@Component
public class InstanceB {
@Autowired
private InstanceA instanceA;
public InstanceA getInstanceA() {
return instanceA;
}
}
3)测试类
public class MainStat {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 一级缓存
private static Map<String, Object> singletoObjects = new ConcurrentHashMap<>(256);
/**
* 二级缓存,为了将成熟的bean和纯净的bean分离
*/
private static Map<String, Object> earlySingletoObjects = new ConcurrentHashMap<>(16);
/**
* 三级缓存
*/
private static Map<String, ObjectFactory> singletoFactories = new ConcurrentHashMap<>(256);
/**
* 循环依赖标识
*/
public static Set<String> singletonsCurrennlyInCreation = new HashSet<>();
public static void loadBeanDefinitions(){
RootBeanDefinition aBeanDefinition = new RootBeanDefinition(InstanceA.class);
RootBeanDefinition bBeanDefinition = new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA",aBeanDefinition);
beanDefinitionMap.put("instanceB",bBeanDefinition);
}
/**
* 获取bean
*
* @param beanName
* @return
*/
public static Object getBean(String beanName) throws Exception {
// 先尝试从一级缓存中通过beanName取bean,如果可以取到,则直接返回,否则尝试从二级缓存中取
Object singletoObject = getSingletoObject(beanName);
if (singletoObject != null) {
return singletoObject;
}
// 正在创建
if (!singletonsCurrennlyInCreation.contains(beanName)) {
singletonsCurrennlyInCreation.add(beanName);
}
Object instanceBean;
synchronized (earlySingletoObjects) {
// 从一级缓存中取,如果取到则直接返回
if(singletoObjects.containsKey(beanName)){
return singletoObjects.get(beanName);
}
// 实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
// 通过无参构造函数
instanceBean = beanClass.newInstance();
// 创建动态代理(此处只会处理A类)
Object obj = new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletoObjects.get(beanName), beanName);
// 将代理对象存入到三级缓存中
singletoFactories.put(beanName, () -> obj);
// 属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 判断该属性上面是否有注解@AutoWired
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 说明属性上面有Autowired
if (annotation != null) {
declaredField.setAccessible(true);
// name = instanceB
String name = declaredField.getName();
// 拿到InstanceB得Bean
Object fileObject = getBean(name);
declaredField.set(instanceBean, fileObject);
}
}
// 判断二级缓存中是否存在,如果A使用了AOP动态代理,可以确保一级缓存中存入的是AOP代理后的对象,而不是纯净的实例bean
if (earlySingletoObjects.containsKey(beanName)) {
instanceBean = earlySingletoObjects.get(beanName);
}
// 将得到的bean放入到一级缓存中
singletoObjects.put(beanName, instanceBean);
}
// remove 二级缓存和三级缓存
return instanceBean;
}
/**
* 先从一级缓存中取,如果没有获取到再从二级缓存中取
* 一级缓存中存的是完整的bean,二级缓存中存入的是独立的bean
*
* @param beanName
* @return
*/
public static Object getSingletoObject(String beanName) {
// 先从一级缓存中获取
Object bean = singletoObjects.get(beanName);
// 一级缓存没有并且循环依赖标识set集合中存在(正在创建),则说明是循环依赖
if (bean == null && singletonsCurrennlyInCreation.contains(beanName)) {
synchronized (singletoObjects) {
// 判断二级缓存中是否存在
bean = earlySingletoObjects.get(beanName);
if (bean == null) {
// 从三级缓存中获取
ObjectFactory factory = singletoFactories.get(beanName);
if (factory != null) {
// 拿到动态代理
bean = factory.getObject();
// 存入到二级缓存中
earlySingletoObjects.put(beanName, bean);
}
}
}
}
return bean;
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 循环创建Bean (key:beanName)
for (String key : beanDefinitionMap.keySet()) {
// 先创建A
getBean(key);
}
InstanceA instanceA = (InstanceA) getBean("instanceA");
instanceA.sayHello();
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Car car = applicationContext.getBean("car",Car.class);
car.say();
}
}
说明测试类中的大体执行流程
A调用getBean()的流程
1、判断缓存中是否存在
2、开始进来一定不存在,往下走
3、判断循环依赖标识里面是否存在,一定不存在
4、给当前线程上一把锁,并尝试从一级缓存中取,此时一定取不到,接着实例化A,得到实例化的bean
5、再调用自定义的JdkProxyBeanPostProcessor类创建A的动态代理
6、将A的动态代理对象存入到三级缓存中,
7、继续往下执行属性赋值,因为A依赖B,所以B再调用getBean(B)
8、B进来重复上面的1,2,3,4(实例化B),5,6步骤
9、因为B中依赖A,所以在进行属性赋值时,A需要调用getBean(A)
10、A进入getBean()方法,先从一级缓存中获取
11、一级缓存中没有,并且缓存标识中存在,说明A循环依赖啦,此时给当前线程加一把锁;
再尝试从二级缓存中获取,二级缓存中没有,再从三级缓存中取,因为在
第6步中已经将A的动态代理实例存入到三级缓存中,所以A可以从三级缓存中获取到A动态代理后的实例
12、获取到A动态代理后的实例,则直接返回;
13、再执行下面的代码,判断二级缓存中是否存在,如果A使用了AOP动态代理,可以确保一级缓存中存入的是AOP代理后的对象,而不是纯净的实例bean
14、将得到的bean存入到一级缓存中
------------------------------------------------------
B调用getBean()的流程
1、判断缓存中是否存在
2、开始进来一定不存在,往下走
3、判断循环依赖标识里面是否存在,一定不存在
4、实例化B,得到实例化的bean
5、再调用自定义的JdkProxyBeanPostProcessor类创建动态代理,因为只在A
中使用了AOP,所以B不会创建代理对象
6、存入到三级缓存中的B实例是纯净的
7、执行属性赋值,因为B依赖了A,所以A会调用getBean()方法,由于A在三级缓存中
已经存在,所以在调用getBean()方法时,可以直接获取到A的代理后的实例对象
8、获取到A实例后,B继续往下执行
10、判断二级缓存中是否存在,此时不存在
11、直接将B的实例存入到一级缓存中
2、Spring中的三级缓存?
2.1 一级缓存
// 一级缓存 这个就是我们单例缓存池,用于保存我们所有的单实例bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
作用:保证一个beanName对应的唯一的Bean完整对象
2.2 二级缓存private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
作用:保证一个beanName对应的唯一的Bean不完整对象。
属性暂时没有值的对象称之为不完整的Bean对象(还没有走完生命周期)
比如A、B、C三个类,B、C中依赖 A, A中依赖B、C
在创建A时,保证注入B C,创建时拿到的A的不完整对象是同一个。
/** 三级缓存 */
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
作用:做一些预备工作。创建bean的时候(第一步实例化产生的对象)先存到三级缓存中。并不直达后面逻辑会不会用,只是防止出现循环依赖且AOP等场景。
- 为什么需要三级缓存?
Spring的方法职责都比较单例,一个方法通常只做一件事,getBean就是获取bean 但是调用创建动态代BeanPostProcessor 是属于create的过程中的, 如果在这里明显代码比较耦合,阅读性也不太好。 所以为了解耦、方法职责单一、方便后期维护, 将调用创建动态代理BeanPostProcessor 放在createBean中是最合适不过了, 但是我们判断当前是否循环依赖还是要写在getSingleton里面啊,这怎么办:
三级缓存 存一个函数接口,函数接口实现 创建动态代理调用BeanPostProcessor。为了避免重复创建, 调用把返回的动态代理对象或者原实例存储在二级缓存,三个缓存完美解决解耦、扩展、性能、代码阅读性。
首先是从单单例池(一级缓存)中查找,找到直接使用。
如果一级缓存中没有,尝试从二级缓存中取,找到直接使用。
如果二级缓存中也没有,再尝试从三级缓存中取,如果取到,则将对象放入到二级缓存中。
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
/**
* 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
*/
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
/**
* 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
* 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态),就是早期对象
*/
singletonObject = this.earlySingletonObjects.get(beanName);
/**
* 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
*/
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
/**尝试从一级缓存中取*/
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
/** 一级缓存中没有,尝试从二级缓存中取*/
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
/**二级缓存中没有,则尝试从三级缓存中取
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**
* 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
* 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
*/
singletonObject = singletonFactory.getObject();
/** 将获取到的代理对象存入到二级缓存中 */
this.earlySingletonObjects.put(beanName, singletonObject);
/** 移除三级缓存中的beanName */
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
4、构造方法和多例导致的循环依赖
4.1 构造方法
上面的代码,使用的都是默认的构造方法来生成的对象,如果是指定特定构造方法,会有什么问题呢?
比如没有用属性注入而是使用构造方法注入,以下代码:
@Component
public class AService {
private BService bService;
public AService(BService bService){
this.bService = bService;
}
public void test(){
System.out.println(this.bService);
}
}
@Component
public class BService {
private AService aService;
public BService(AService aService){
this.aService = aService;
}
}
创建AService的bean对象,只能使用给出的构造方法,但是构造方法里面需要一个BService,发现找不到。
找不到就会去创建BService对象,也是只能使用给出的构造方法,需要一个AService的bean,不能再去创建AService了,但是又没有办法得到一个AService对象。
也就是说,在创建普通对象的时候就失败了,所有没有办法生成lambda表达式,更没有办法做其他事。
- 解决方法:
加上@Lazy注解即可。Spring会传入一个@Lazy对应的代理对象参数进入
- 如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存;
- 如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)