IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring循环依赖 | Spring三级缓存 | 看完必有收获 -> 正文阅读

[Java知识库]Spring循环依赖 | Spring三级缓存 | 看完必有收获

0. 举个栗子:

?所谓循环依赖,就是A中依赖了B,而B又依赖了A,如下

@Service
public class BeanServiceA {
    private String name = "serviceA";

    @Autowired
    BeanServiceB beanServiceB;

    public void testMethod() {
        System.out.println();
    }
}

@Service
public class BeanServiceB {
    private String name = "serviceA";

    @Autowired
    BeanServiceA beanServiceA;
}

@Component
public class AopAspectConfiguration {

    @Pointcut("execution(public * com.aop.BeanServiceA.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
        System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }
}    

?本文举栗中A要进行代理,而B不需要

1. Bean创建过程及循环依赖解决:

?这部分需要对Bean创建的生命周期有所了解,可以参考生命周期系列文章
·
?先介绍一下循环依赖涉及的重点方法DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 从一级缓存singletonObjects获取bean
	Object singletonObject = this.singletonObjects.get(beanName);
	// 一级缓存没有,判断该bean是否在创建中,通过Set的contains来判断
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			// 从二级缓存中获取bean
			singletonObject = this.earlySingletonObjects.get(beanName);
			// 二级缓存没有&&允许提前引用
			if (singletonObject == null && allowEarlyReference) {
				// 从三级缓存中获取lambda表达式
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					// 调用三级缓存的lambda表示获取早期不完整对象
					singletonObject = singletonFactory.getObject();
					// 写入二级缓存
					this.earlySingletonObjects.put(beanName, singletonObject);
					// 三级缓存移除该bean的lambda表达式
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

public boolean isSingletonCurrentlyInCreation(String beanName) {
	return this.singletonsCurrentlyInCreation.contains(beanName);
}

?1.1. BeanServiceA的创建:

?在创建bean时,会调用doGetBean方法,首先通过getSingleton方法从缓存中看是否能获取到该bean

  • a. 先从一级缓存singletonObjects中获取,发现获取不到,然后看是否在创建中,显然初次创建时不成立,即getSingleton返回null
  • b. 调用第14行的getSingleton方法触发createBean回调,进行bean的生命周期
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
		... ... ...
		
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			... ... ... 
		}else {
			... ... ...
			try {
				... ... ...
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
			... ... ...
		}
		return (T) bean;
	}

?在第14行的getSingleton,会将当前beanName放入singletonsCurrentlyInCreation,表示当前bean正在创建

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				... ... ...
				// 会将当前beanName放入singletonsCurrentlyInCreation,表示当前bean正在创建
				beforeSingletonCreation(beanName);
				... ... ...
				try {
					// lambda表达式回到createBean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
			... ... ...	
			}
			return singletonObject;
		}
	}

?1.1.1. 实例化BeanServiceA不完整对象:

  • 一般通过createBeanInstancec实例化不完整的BeanServiceA对象
  • 将不完整对象以及BeanDefinition代表的lambda表达式写入三级缓存
  • 属性填充BeanServiceB
  • 初始化BeanServiceA时调用AOP后置处理器进行AOP处理
  • 处理提前暴露的场景,保证返回同一个代理对象
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
		throws BeanCreationException {

	... ... ...
	if (instanceWrapper == null) {
		// a. 一般通过createBeanInstancec实例化不完整的BeanServiceA对象
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	... ... ...
	// 默认单例&&默认循环引用&&该bean正在创建,条件成立
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		... ... ...
		// b. 将不完整对象以及BeanDefinition代表的lambda表达式写入三级缓存
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
		// c. 属性填充BeanServiceB
		populateBean(beanName, mbd, instanceWrapper);
		// d.初始化时调用AOP后置处理器进行AOP处理
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}
	... ... ...
	// e.处理提前暴露的场景,保证返回同一个代理对象
	if (earlySingletonExposure) {
		// 见上文第1节的节速,由于第二个参数是false,所以只会查到第二季缓存
		// 所以这里就是查看第二级缓存能不能取到值,取到就意味着涉及提前AOP
		Object earlySingletonReference = getSingleton(beanName, false);
		// 涉及提前AOP,从二级缓存中获取提前AOP的代理对象
		if (earlySingletonReference != null) {
			if (exposedObject == bean) {
				// 保证循环依赖且涉及AOP时,返回同一个代理对象,下文有结束
				exposedObject = earlySingletonReference;
			}
			... ... ...
		}
	}
	return exposedObject;
}

?addSingletonFactory将不完整对象以及BeanDefinition代表的lambda表达式写入三级缓存

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			// 将不完整对象以及BeanDefinition代表的lambda表达式写入三级缓存
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

?1.1.2. 属性填充BeanServiceB:

?关于属性填充方法的详细介绍可参考链接:属性填充populateBean

?当填充BeanServiceB会重复上文第1节中的内容:

  • a. 先从一级缓存singletonObjects中获取,发现获取不到,然后看是否在创建中,显然初次创建时不成立,即getSingleton返回null
  • b. 调用第14行的getSingleton方法触发createBean回调,进行bean的生命周期
  • c. 实例化BeanServiceB的不完整对象,并将lambda写入三级缓存
  • d. 属性填充BeanServiceA(见下文1.1.2.1)
  • e. initializeBean初始化BeanServiceB

?1.1.2.1. 循环依赖处理属性填充BeanServiceA:

?同样,会重复上文第1节的内容,但此时会有不一样的处理:

?a). 先从一级缓存singletonObjects中获取,发现获取不到,然后看是否在创建中
·
??显然正在创建,singletonsCurrentlyInCreation有beanServiceA

?b). 从二级缓存中获取 → 获取不到 → 从三级缓存获取 → lambda表达式回调(见下文1.1.2.1.1)

?c). 将不完整对象BeanServiceA写入二级缓存,三级缓存删除该对象lambda表达式(上文1中getSingleton方法)

?d). 返回不完整的BeanServiceA对象

?1.1.2.1.1. AbstractAutoProxyCreator#getEarlyBeanReference:

?这里涉及Aop,关于Aop源码有兴趣可以查看链接:Aop代理过程

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
	Object exposedObject = bean;
	if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				// 调用后置处理器的getEarlyBeanReference进行提前暴露bean
				exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
			}
		}
	}
	return exposedObject;
}

?lambda回调会调用后置处理器的getEarlyBeanReference,来获取不完整的bean

  • 如果当前依赖的bean不涉及Aop,则返回实例化时创建的不完整bean对象
  • 如果当前依赖的bean涉及Aop,则返回一个代理该不完整bean的代理对象
  • 本处返回实例化创建的不完整beanServiceA对象
public Object getEarlyBeanReference(Object bean, String beanName) {
	Object cacheKey = getCacheKey(bean.getClass(), beanName);
	// 写入earlyProxyReferences,在后面postProcessAfterInitialization会用到
	this.earlyProxyReferences.put(cacheKey, bean);
	// 判断是否创建代理对象
	return wrapIfNecessary(bean, beanName, cacheKey);
}

?1.1.2.2. 初始化BeanServiceB时调用AOP后置处理器进行AOP处理

?属性填充beanServiceA后,此时填充的beanServiceA是不完整的对象

在这里插入图片描述

?在initializeBean初始化调用后置处理器的postProcessAfterInitialization方法

在这里插入图片描述

?由于BeanServiceB不涉及AOP,所以返回原始的B对象,此时填充的beanServiceA还是不完整的对象

  • B不涉及提前AOP,所以earlyProxyReferences没有beanServiceB(见上文1.1.2.1.1)
  • remove返回null,条件成立,执行wrapIfNecessary方法
  • 由于beanServiceB不涉及AOP,所以返回原始的B对象

?1.1.2.3. 处理提前暴露的场景,保证返回同一个代理对象

?BeanServiceB不涉及AOP,getSingleton返回null,所以直接返回原始对象exposedObject

在这里插入图片描述

?1.1.2.4. BeanServiceB对象写入一级缓存,移除二、三级缓存

?beanServiceB生命周期执行完,返回到1.1节第14行的getSingleton方法,此时返回的还是不完整的beanServiceB对象
·
?singletonsCurrentlyInCreation移除bean,表明不再是正在创建的bean

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				... ... ...
				// 会将当前beanName放入singletonsCurrentlyInCreation,表示当前bean正在创建
				beforeSingletonCreation(beanName);
				... ... ...
				try {
					// lambda表达式回到createBean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				... ... ..
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					// singletonsCurrentlyInCreation中移除beanName,表示该bean不是正在创建了
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// 将不完整的beanServiceB对象写入一级缓存,移除二、三级缓存
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

?beanServiceB“不完整对象”写入一级缓存,移除二、三级缓存

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		// 写入一级缓存
		this.singletonObjects.put(beanName, singletonObject);
		// 移除二、三级缓存
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}

?1.1.3. BeanServiceA属性填充BeanServiceB(完整bean)

?此时ioc容器已经有了beanServiceB,虽然暂时还是不完整的
·
?但是,当beanServiceA填充完beanServiceB后,使得互相循环依赖对方,此时二者都变成了完整的bean
·
?此时一级缓存的beanServiceB也由不完整的bean变成了完整的bean,因为是同一个地址

在这里插入图片描述

?A涉及AOP,所以循环依赖时,A会进行提前AOP,所以B中填充的是A的代理对象
·

在这里插入图片描述

?当A填充完B时,构成互相循环依赖对方

在这里插入图片描述

?1.1.4. 初始化BeanServiceA时调用AOP后置处理器进行AOP处理

  • 由于beanServiceA提前AOP了,所以所以earlyProxyReferences有beanServiceA(见上文1.1.2.1.1)
  • remove返回原始的bean,即earlyProxyReferences对应的value,条件不成立,直接==返回原始bean ==

?1.1.5. 处理提前暴露的场景,保证返回同一个代理对象

?beanServiceA涉及提前AOP,getSingleton返回保存在二级缓存中提前AOP的代理对象A

在这里插入图片描述

?这里面有exposedObject == bean判断,这就是为什么上述1.1.4中AOP后置处理器返回原始bean的原因
`
?条件成立,将代理对象赋值放回,保证返回同一个代理对象,即B中的A和A都是同一个代理对象

?1.2. BeanServiceA完整对象写入一级缓存,移除二、三级缓存

?同1.1.2.4节一样,beanServiceA生命周期执行完,会将beanServiceA写入一级缓存,移除二、三级缓存

2. 整体流程:

?2.1. 涉及循环依赖&&涉及AOP的场景

?该场景大概流程如下图,详细可见上文介绍

在这里插入图片描述

?2.2. 不涉及AOP的循环依赖场景

  • 不涉及AOP,那么A进行lambda表达式回调后返回就A的原始对象,保存到二级缓存中,所以B属性填充后的A也是A的原始对象
  • 当A属性填充完后,A和B相互依赖,使得二者都是完整的对象,可见上文1.1.3的图

?2.3. 不涉及循环依赖的AOP场景

  • 不涉及循环依赖,也就不涉及提前AOP,正常A经过实例化–属性填充–初始化
  • 在初始化时通过AOP后置处理器创建代理对象返回,在上文1.1.5节,二级缓存返回null,最终直接返回代理对象A

?2.4. 不涉及循环依赖&&不涉及AOP场景

  • 不涉及循环依赖,也就不涉及提前AOP,正常A经过实例化–属性填充–初始化
  • 不涉及AOP,则在初始化时通过AOP后置处理器直接返回原始A对象,在上文1.1.5节,二级缓存返回null,最终返回原始A对象

3. 要点讲解:

?3.1. 什么是三级缓存?

	// 一级缓存,用于存放完整的单例bean
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	//二级缓存,用于存放原始(不完整)的对象,用于解决循环依赖
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	// 三级缓存,存放函数式接口,用于解决循环依赖
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

?3.2. 为什么二三级缓存用HashMap,而不像一级缓存使用ConcurrentHashMap

?现有逻辑是,二三级缓存的操作是在synchronized代码块里面操作的(见下方10、11行代码)
·
?这已经是线程安全的了,所以没有必要使用ConcurrentHashMap

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	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;
}

?那为什么要用synchronized而不直接用ConcurrentHashMap来保证线程安全呢?

  • 二级缓存put的同时要保证三级缓存remove;三级缓存put时要保证二级缓存remove,也就是说二三级缓存操作要保证原子性
  • 因为要保证同一个bean是单例的,不然都会lambda回调创建bean,就不是单例的了
  • 如果使用ConcurrentHashMap并不能保证二三级缓存操作的原子性,所以要用synchronized
  • 这三级缓存都是在synchronized内操作的,至于一级缓存为什么用ConcurrentHashMap,可能其他场景的原因吧,我也不不清楚

?3.3. 为什么二三级缓存初始容量是16,一级缓存是256

?上面3.2分析可知,为保证单例,二三级缓存是一次性的,所以没必要那么大容量
·
?而一级缓存是保存完整的单例bean,ioc管理的bean数量也不小,所以容量就大了点

?3.4. 什么要第三级缓存?

?主要用于循环依赖的bean需要AOP时提前AOP
·
?如果没有第三级缓存,那么getSingleton就返回null,就会再次传教A,导致一直循环创建,现有逻辑就不对.

在这里插入图片描述

?那如果将实例化的原始对象放入二级缓存呢?

  • 没有第三级缓存,就无法提前AOP,则B属性填充完的A为A原始对象
  • 而A在属性填充完B后,需要进行AOP,则经过AOP后置处理器会去创建代理对象A返回
  • 这就导致B的属性A不是代理对象,而A却是代理对象,这与Spring的单例bean是矛盾的。

?3.5. 第三级缓存是怎么处理AOP的?

  • 循环依赖中A需要AOP,那么从三级缓存中取出lambda表达式回调进行提前AOP,将提前AOP的代理对象A写入二级缓存
  • B的生命周期完成后,B中的A就代理对象A
  • 当A属性填充完,AOP后置处理器判断A是否提前AOP了,是就直接返回A(并不是返回代理的A)
  • 然后从二级缓存中取出代理对象A返回,见上文1.1.5
  • 这里后置处理器不直接返回代理对象A,是因为代理对象A要从二级缓存获取,不能在后置处理器再创建代理返回,不然就导致B中的A和返回的A不是同一个代理对象

?3.6. 不能解决哪写场景的循环依赖

?可以查看参考链接第三节,主要有以下几种

在这里插入图片描述

1)构造器注入的循环依赖

  • 因为实例化A时,就要去依赖注入B;B实例化时也去依赖注入A,导致singletonsCurrentlyInCreation都有A和B,那么循环依赖时,就抛异常了

在这里插入图片描述

2)原型(多例)的bean注入的循环依赖

  • 没有用缓存,每次都会生成一个新对象,无法解决

?3.7. Spring是怎么解决循环依赖的

?上述栗子就是单例的属性注入的循环依赖,通过三级缓存来解决的循环依赖,

  • 主要是循环依赖涉及AOP时,通过第三级缓存进行提前AOP,保证注入的是同一个AOP代理对象

?此外@Lazy注解好像也涉及循环依赖解决,有兴趣可以百度,或参考看看,本人还不怎么了解

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 07:51:35  更:2021-09-19 07:51:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:59:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码