目录
一、Spring手写循环依赖
1、循环依赖问题的出现
2、循环依赖问题的解决 -> 一级缓存
3、循环依赖问题的解决 -> 二级缓存
4、循环依赖问题的解决(动态代理) -> 三级缓存
二、Spring循环依赖源码分析
三、Spring 有没有解决构造函数的循环依赖和多实例的循环依赖?
一、Spring手写循环依赖
1、循环依赖问题的出现
代码示例,A实例中有B
@Component
public class InstanceA {
@Autowired
private InstanceB instanceB;
public InstanceB getInstanceB() {
return instanceB;
}
public void setInstanceB(InstanceB instanceB) {
this.instanceB = instanceB;
}
public InstanceA(InstanceB instanceB) {
this.instanceB = instanceB;
}
public InstanceA() {
System.out.println("InstanceA实例化");
}
}
B实例中有A
@Component
public class InstanceB {
@Autowired
private InstanceA instanceA;
public InstanceA getInstanceA() {
return instanceA;
}
public void setInstanceA(InstanceA instanceA) {
this.instanceA = instanceA;
}
public InstanceB(InstanceA instanceA) {
this.instanceA = instanceA;
}
public InstanceB() {
System.out.println("InstanceB实例化");
}
}
手写循环依赖
public class MainStart {
// beanDefinition容器
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 一级缓存:存放完整的Bean
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA", beanDefinitionA);
beanDefinitionMap.put("instanceB", beanDefinitionB);
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 先创建A
getBean(key);
}
}
// 通过beanName获取Bean
public static Object getBean(String beanName) throws Exception {
// 开始创建Bean -> 实例化、属性赋值、初始化
// 1-实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数实例化Bean
// 2-属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();// 获得某个类的所有声明的字段
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 如果属性上面有Autowired注解
if (annotation != null) {
declaredField.setAccessible(true); // 设置访问权限
// instanceB -> byName/byType/byConstructor
String name = declaredField.getName(); // 获取有Autowired注解的实例的名字,去实例化该Bean
Object fileObject = getBean(name); // 递归——>获取Bean
declaredField.set(instanceBean, fileObject); // 设置属性值
}
}
// 3-初始化 -> init-method,执行一些初始化回调方法,省略...
// 4-把Bean添加到一级缓存
singletonObjects.put(beanName, instanceBean);
return instanceBean;
}
}
执行实例化A的创建过程,会出现循环依赖,导致栈溢出
循环依赖的图示
2、循环依赖问题的解决 -> 一级缓存
为了打破循环依赖,改动一下代码逻辑,在创建Bean前,先去一级缓存中去获取Bean实例,如果实例已经在缓存中,就不再循环的去创建。同样,为了能在循环之前拿到缓存中的Bean,需要在属性赋值前就把创建好的实例放入到缓存中(尽管此时的实例还没有赋值),图示如下
代码改动
public class MainStart {
// beanDefinition容器
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 一级缓存:存放完整的Bean
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA", beanDefinitionA);
beanDefinitionMap.put("instanceB", beanDefinitionB);
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 注册Bean的后置处理器,省略...
// 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 先创建A
getBean(key);
}
}
// 通过beanName获取Bean
public static Object getBean(String beanName) throws Exception {
// 增加出口:先从缓存中拿
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 开始创建Bean -> 实例化、属性赋值、初始化
// 1-实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数实例化Bean
// 3-新增->把Bean添加到一级缓存
// 问题:如果在此处put,可以解决循环依赖,但是多线程环境下会拿到没有属性赋值的不完整Bean
// 除非对整个创建过程加锁->低效率
singletonObjects.put(beanName, instanceBean);
// 2-属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();// 获得某个类的所有声明的字段
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 如果属性上面有Autowired注解
if (annotation != null) {
declaredField.setAccessible(true); // 设置访问权限
// instanceB -> byName/byType/byConstructor
String name = declaredField.getName(); // 获取有Autowired注解的实例的名字,去实例化该Bean
Object fileObject = getBean(name); // 递归——>获取Bean
declaredField.set(instanceBean, fileObject); // 设置属性值
}
}
// 4-初始化 -> init-method,执行一些初始化回调方法,省略...
// 5-再一次把Bean添加到一级缓存
singletonObjects.put(beanName, instanceBean);
return instanceBean;
}
// 从缓存中获取Bean
public static Object getSingleton(String beanName) {
// 从一级缓存中拿
Object bean = singletonObjects.get(beanName);
return bean;
}
}
测试结果
不过,这种在属性赋值前,就把Bean存放到一级缓存的做法,在多线程环境下,会使得某些线程获取的Bean,可能是不完整的Bean(实例还没有赋值)。
3、循环依赖问题的解决 -> 二级缓存
为了避免在多线程环境下,获取到不完整的Bean,引入二级缓存,示例图如下?
代码改动如下,把原来未赋值的Bean改存到二级缓存中
public class MainStart {
// beanDefinition容器
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 一级缓存:存放完整的Bean
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:存放不完整的Bean(还没有属性赋值)——>区分完整的Bean和不完整的Bean
public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
/**
* 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA", beanDefinitionA);
beanDefinitionMap.put("instanceB", beanDefinitionB);
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 注册Bean的后置处理器
// 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 先创建A
getBean(key);
}
}
// 通过beanName获取Bean
public static Object getBean(String beanName) throws Exception {
// 增加出口:先从缓存中拿
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// 开始创建Bean -> 实例化、属性赋值、初始化
// 1-实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数实例化Bean
// 添加到二级缓存
earlySingletonObjects.put(beanName,instanceBean);
// 2-属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();// 获得某个类的所有声明的字段
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 如果属性上面有Autowired注解
if (annotation != null) {
declaredField.setAccessible(true); // 设置访问权限
// instanceB -> byName/byType/byConstructor
String name = declaredField.getName(); // 获取有Autowired注解的实例的名字,去实例化该Bean
Object fileObject = getBean(name); // 递归——>获取Bean
declaredField.set(instanceBean, fileObject); // 设置属性值
}
}
// 3-初始化 -> init-method,执行一些初始化回调方法,省略...
// 把Bean添加到一级缓存
singletonObjects.put(beanName, instanceBean);
return instanceBean;
}
// 从缓存中获取Bean
public static Object getSingleton(String beanName) {
// 先从一级缓存中拿
Object bean = singletonObjects.get(beanName);
// 一级缓存为Null,从二级缓存中获取
if(bean == null){
bean = earlySingletonObjects.get(beanName);
}
return bean;
}
}
从代码层级来说,解决循环依赖,我们使用二级缓存就可以了,但是为什么要有三级缓存呢?
4、循环依赖问题的解决(动态代理) -> 三级缓存
假设实例A需要创建动态代理,对实例A代码做如下改造
// 接口
public interface IApi {
void say();
}
// 实例A实现接口
@Component
public class InstanceA implements IApi {
@Autowired
private InstanceB instanceB;
public InstanceB getInstanceB() {
return instanceB;
}
public void setInstanceB(InstanceB instanceB) {
this.instanceB = instanceB;
}
public InstanceA(InstanceB instanceB) {
this.instanceB = instanceB;
}
public InstanceA() {
System.out.println("InstanceA实例化");
}
@Override
public void say() {
System.out.println("I'm A");
}
}
动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkDynamicProxy implements InvocationHandler {
private Object target;
public JdkDynamicProxy(Object target) {
this.target = target;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
对实例A进行动态代理 -> 模拟AOP,通过后置处理器来进行动态代理
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 创建JDK动态代理
*/
@Component
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
// 假设:A 被切点命中 需要创建代理 @PointCut("execution(* *..InstanceA.*(..))")
if (bean instanceof InstanceA) {
JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(bean);
return jdkDynamicProxy.getProxy();
}
return bean;
}
}
一般来说,对单例Bean创建动态代理,都是在初始化之后,但是对于循环依赖来说,动态代理的创建需要在实例化之后就创建。比如本例中的Bean A,如果仍然选择在初始化后创建代理,那么实例Bean B中拿到的Bean A就不是代理对象。
如上图,在循环依赖中,如果把实例化后的Bean,创建动态代理后然后再放入二级缓存,此时,关于动态代理的循环依赖问题就已经解决了,?那么为什么还要三级缓存?
试想下,Spring中创建动态代理是通过后置处理器来实现的,如果在创建Bean的过程中,再另外去调用后置处理器创建代理,代码风格上会不会存在不合理?实例化后需要创建动态代理,初始化后又要创建动态代理,两个逻辑能不能合在一起,都放在初始化后创建呢?
答案是,能,可以借助三级缓存和函数式接口(延迟创建动态代理)来实现。
下边需要使用一个函数式接口(ObjectFactory)
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* 返回此工厂管理的对象的实例(可能是共享的或独立的)
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}
通过三级缓存来解决循环依赖,手写代码示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class MainStart {
// beanDefinition容器
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 一级缓存:存放完整的Bean
public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
// 二级缓存:存放不完整的Bean(还没有属性赋值)——>区分完整的Bean和不完整的Bean
public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
// 三级缓存 -> 存函数式接口(延迟调用)
public static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();
// 循环依赖标识 -> 正在创建的Bean集合
public static Set<String> singletonsCurrentlyInCreation = new HashSet<>();
/**
* 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA", beanDefinitionA);
beanDefinitionMap.put("instanceB", beanDefinitionB);
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 注册Bean的后置处理器...省略...
// 循环创建Bean
for (String key : beanDefinitionMap.keySet()) {
// 先创建A
getBean(key);
}
InstanceA instanceA = (InstanceA) getBean("instanceA");
instanceA.say();
}
// 通过beanName获取Bean
public static Object getBean(String beanName) throws Exception {
// 增加出口:先从缓存中拿
Object singleton = getSingleton(beanName);
if (singleton != null) {
return singleton;
}
// singletonsCurrentlyInCreation -> 正在创建的Bean集合
if (!singletonsCurrentlyInCreation.contains(beanName)) {
singletonsCurrentlyInCreation.add(beanName);
}
// 开始创建Bean -> 实例化、属性赋值、初始化
// 1-实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数实例化Bean
// 假设A 使用了Aop @PointCut("execution(* *..InstanceA.*(..))") 要给A创建动态代理
// 创建动态代理(BeanPostProcessor) -> 解耦合
// 三级缓存:存在循环依赖的Bean在实例化后创建proxy(延迟),正常的Bean在初始化后创建 -> 需要判断当前是不是循环依赖
if(singletonsCurrentlyInCreation.contains(beanName)){
// 为了简便,先把循环依赖的对象放入二级缓存(源码中没有此步骤)
earlySingletonObjects.put(beanName, instanceBean);
singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName), beanName));
}
// 2-属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();// 获得某个类的所有声明的字段
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 如果属性上面有Autowired注解
if (annotation != null) {
declaredField.setAccessible(true); // 设置访问权限
// instanceB -> byName/byType/byConstructor
String name = declaredField.getName(); // 获取有Autowired注解的实例的名字,去实例化该Bean
Object fileObject = getBean(name); // 递归——>获取Bean
declaredField.set(instanceBean, fileObject); // 设置属性值
}
}
// 3-初始化 -> init-method,执行一些初始化回调方法,省略...
// AOP -> 正常情况下会在初始化之后创建proxy,但是如果是循环依赖, B里面的A就不是proxy
// 由于递归完后A还是原实例,所以要从二级缓存中拿到proxy
if (earlySingletonObjects.containsKey(beanName)) {
instanceBean = earlySingletonObjects.get(beanName);
}
// 把Bean添加到一级缓存,移除二、三级缓存
singletonObjects.put(beanName, instanceBean);
singletonFactories.remove(beanName);
earlySingletonObjects.remove(beanName);
return instanceBean;
}
// 从缓存中获取Bean
public static Object getSingleton(String beanName) {
// 先从一级缓存中拿
Object bean = singletonObjects.get(beanName);
// 一级缓存没有,说明是循环依赖的Bean
if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
bean = earlySingletonObjects.get(beanName);
// 如果二级缓存没有,尝试从三级缓存获取
if (bean == null) {
// 从三级缓存中拿到函数式接口——>然后执行
ObjectFactory factory = singletonFactories.get(beanName);
if(factory != null){
// 返回此工厂管理的对象的实例->拿到动态代理(在这里真正创建,执行代理)
bean = factory.getObject();
// 放入二级缓存
earlySingletonObjects.put(beanName, bean);
// 移除三级缓存,步骤省略...
singletonFactories.remove(beanName);
}
}
}
return bean;
}
}
运行结果:
三级缓存,实际上缓存的是函数式接口。最终的图示如下
二、Spring循环依赖源码分析
从创建Bean开始,我们看一下Spring源码的处理过程,首先从缓存中去获取Bean
继续往下看,如果没有获取到,那么就去创建Bean
注意这里有两个方法:
- getSingleton(String beanName, ObjectFactory<?> singletonFactory);
- createBean(beanName, mbd, args);? // 真正实例化Bean
其中 createBean(beanName, mbd, args) 会在 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法中被回调
接下来,进入getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法
该方法中,首先会为正在创建的Bean添加到一个标记集合:singletonsCurrentlyInCreation.add(beanName)
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
然后,会调用创建Bean的函数式接口,真正的创建Bean,最后,把完整的Bean,放入到一级缓存当中
addSingleton(String beanName, Object singletonObject) 详情:
那么,createBean(beanName, mbd, args) 中具体又做了什么呢?
进入createBean(beanName, mbd, args) 方法中,有一个 doCreateBean(beanName, mbdToUse, args) 方法
进入doCreateBean(beanName, mbdToUse, args) 方法,实例化后,如果是早期对象,那么会放入到三级缓存
最后,初始化后的早期对象实例,仍然会从缓存中获取
其中,解决循环依赖的核心就是这个方法:
getSingleton(String beanName, boolean allowEarlyReference)
正式调用三级缓存中的函数式接口,生成代理对象存入到二级缓存,最后获得早期对象返回去创建其他的Bean,然后经过一系列的循环创建,最终生成完整的实例对象,存入一级缓存。至此,循环依赖解决完毕。
三、Spring 有没有解决构造函数的循环依赖和多实例的循环依赖?
Spring 无法解决构造函数的循环依赖,因为构造函数的循环依赖出现在实例创建之前,而Spring的循环依赖是通过实例的缓存来解决的,前提是需要有实例。
同样,Spring也无法解决多例Bean的循环依赖,当多例Bean存在循环依赖时,直接抛出异常
原因是,多例 Bean 创建时,根本不会把创建完的实例存放到缓存当中,对比单例的创建过程,多例Bean并没有被 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 回调
而且,也不满足解决循环依赖的单例Bean的条件
|