示例代码
@Component
public class ClassRoom {
private String name = "天下第一班";
@Autowired
private Student student;
@Override
public String toString() {
return "ClassRoom{" +
"name='" + name + '\'' +
'}';
}
}
@Component
public class Student {
private String name = "张三";
@Autowired
private ClassRoom classRoom;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
@ComponentScan
public class CircularReferencesTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(CircularReferencesTest.class);
context.setAllowCircularReferences(true);
context.refresh();
System.out.println(context.getBean(Student.class));
System.out.println(context.getBean(ClassRoom.class));
context.close();
}
}
-
ClassRoom
和Student
互相引用; -
context.setAllowCircularReferences(true)
在refresh前可以设置是否允许循环引用,默认是允许;
源码分析
循环引用-1.png-
finishBeanFactoryInitialization(beanFactory)
此时BeanFactory 已经创建完成,并且所有的实现了 BeanFactoryPostProcessor 接口的 Bean都已经初始化并且其中的 postProcessBeanFactory(factory) 方法已经得到回调执行了。而且 Spring 已经“手动”注册了一些特殊的 Bean,如 environment、systemProperties 等。剩下的就是初始化 singleton beans 了,如果没有设置懒加载,那么 Spring 会在这里初始化所有的 singleton beans。 -
getBean(beanName)
遍历 beanDefinitionNames 通过 getBean(beanName) 去初始化 singleton beans。
下面重点看下AbstractBeanFactory.doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 转换beanName,这里name有可能是别名也可能是FactoryBean
final String beanName = transformedBeanName(name);
Object bean;
// 尝试从缓存中加载单例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
......
// 返回对应实例,有时候存在BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例
// 这里其实就是用于检测当前 bean 是否是 FactoryBean 类型,如果是则需要调用 FactoryBean 中的
// getObject()作为返回值,例如 FactoryBean的使用
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 原型模式循环依赖直接抛错,只有在单例情况下才会尝试解决循环依赖,即 A 中有 B 的属性,B 中有 A的属性,
// 依赖注入时,会产生 A 未创建完对于 B 的创建再次返回创建 A,的情况
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
......
// 实例化依赖 bean 之后,开始实例化 bean 本身
if (mbd.isSingleton()) {
// 缓存中不存在,开始直接去加载单例 Bean
sharedInstance = getSingleton(beanName, () -> {
try {
// getSingleton通过回调该方法,真正去创建 Bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
......
}
......
return (T) bean;
}
-
Object sharedInstance = getSingleton(beanName)
尝试从缓存中加载 bean,稍后详细分析; -
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)
返回对应实例,有时候存在 BeanFactory 的情况并不是直接返回实例本身,这里其实就是用于检测当前 bean 是否是 FactoryBean 类型,如果是则需要调用 FactoryBean 中的 getObject() 作为返回值,例如 FactoryBean的使用; -
isPrototypeCurrentlyInCreation(beanName)
当前 beanName 的原型模式的 bean 正在创建,说明原型模式存在循环依赖,直接抛出异常,只有在单例情况下才会尝试解决循环依赖; -
sharedInstance = getSingleton
缓存中不存在,开始直接去加载单例 Bean;
小结一下就是:首先从缓存中获取 bean 实例,存在的话返回;否则通过getSingleton(String beanName, ObjectFactory<?> singletonFactory)
方法并注册回调函数createBean(beanName, mbd, args)
去创建 bean 实例,其中包含缓存的维护以及循环引用的处理逻辑;
再来重点看下getSingleton(beanName)
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) {
// 当某些方法需要提前初始化的时候会调用 addSingletonFactory 将对应的
// ObjectFactory 初始化策略存储在 singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用预先设定的 getObject 方法
singletonObject = singletonFactory.getObject();
// 存储在缓存中,earlySingletonObjects 和 singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这里涉及3个Map1个Set
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
-
singletonsCurrentlyInCreation
存放当前正在创建的单例 bean,bean 创建完成后移除,用来判断是否正在创建; -
singletonObjects
存放直接可用的成品 bean; -
earlySingletonObjects
存放半成品的 bean(已经调用构造,但属性尚未赋值),提前曝光的 bean,用于解决循环依赖; -
singletonFactories
半成品工厂,存放 ObjectFactory<?> 类型对象,用来生成半成品 bean 放入 earlySingletonObjects;
小结一下这里的逻辑(allowEarlyReference默认为true,这里忽略):
- 从
singletonObjects
中获取到成品 bean 则直接返回,否则goto 2; - 通过
singletonsCurrentlyInCreation
判断当前 bean 是否正在创建,不是则返回 null 到 doGetBean 中进行创建,是则goto 3; - 从
earlySingletonObjects
中获取提前曝光的半成品 bean,存在则直接返回,否则goto 4; - 从
singletonFactories
中获取单例 bean 工厂,用来生成半成品 bean,不存在则返回null,否则goto 5; - 通过预先设定的 getObject 方法生成半成品 bean,并存储放入
earlySingletonObjects
,由于单例bean 已经生成,则对应的 SingletonObject从singletonFactories
中移除;
再回到doGetBean
看下缓存中不存在需要去创建的逻辑
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
// 再次检查是否已经加载过
if (singletonObject == null) {
......
// 加载单例前记录加载状态,beanName 加到 singletonsCurrentlyInCreation 中
beforeSingletonCreation(beanName);
boolean newSingleton = false;
......
try {
// 通过回调方法 singletonFactory 实例化 Bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
......
finally {
// 加载完成 beanName 从singletonsCurrentlyInCreation 中移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到 singletonObjects / registeredSingletons,
// 从 singletonFactories / earlySingletonObjects 中删除
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
-
beforeSingletonCreation(beanName)
创建 bean 之前存入singletonsCurrentlyInCreation
中,到这里如果已经存在当前 bean 则会抛出循环引用的异常,对应context.setAllowCircularReferences(false)
的场景;
Requested bean is currently in creation: Is there an unresolvable circular reference?
-
singletonFactory.getObject()
通过 doGetBean 中注册的回调函数创建 bean,下面详细分析; -
afterSingletonCreation(beanName)
维护singletonsCurrentlyInCreation
,即创建后从里面移除; -
addSingleton(beanName, singletonObject)
添加到singletonObjects
,并从singletonFactories / earlySingletonObjects
中删除,及bean 创建完成后加入成品Map,同时从半成品Map和半成品工厂中移除当前 bean;
小结一下:这里就是真正去创建 bean,并且维护3个Map1个Set;
下面再来分析回调函数中直接创建 bean的逻辑createBean(beanName, mbd, args)
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
......
if (instanceWrapper == null) {
// 创建 Bean 实例,将 BeanDefinition 转换成 BeanWrapper
// 1. 如果存在工厂方法则使用工厂方法进行初始化
// 2. 一个类有多个构造方法,每个构造方法都有不同的参数,所以需要根据参数锁定构造函数并进行初始化
// 3. 如果既不存在工厂方法也不存在带有参数的构造函数,使用默认的构造函数进行实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
......
/**
* 重要:::A 依赖 B,B 又依赖 A,如何解决循环依赖
* 在 B 中创建依赖 A 时通过 ObjectFactory 提供的实例化方法来中断 A 中的属性填充,使 B 中持有的 A 仅仅是刚刚初始化并没有填充任何属性的 A,
* 而正在初始化 A 的步骤还是在最开始创建 A 的时候进行的,但是因为A与B中的A所代表的属性地址是一样的,所以A中创建好的属性填充自然可以通过
* B 中的A获取,这样就解决了循环依赖的问题
*/
// 是否需要提早曝光:单例 & 允许循环依赖 & 当前 bean 正在创建中,检测循环依赖 allowCircularReferences默认 true
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName)); // DefaultSingletonBeanRegistry 类的 getSingleton 函数的 beforeSingleton 和 afterSingleton中来进行状态的记录与移除
if (earlySingletonExposure) {
// 为避免后期循环依赖可以在 bean 初始化完成前将创建实例的 ObjectFactory 加入 singletonFactories registeredSingletons
addSingletonFactory(beanName, () -> {
// 对 bean 再一次依赖引用,主要应用 SmartInstantiationAware BeanPostProcessor
// AOP 就是在这里将 advice 动态织入 bean 中,若没有则直接返回bean,不做任何处理
return getEarlyBeanReference(beanName, mbd, bean);
});
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 这一步也是非常关键的,这一步负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值
// 对 bean 进行填充,将各个属性值注入,其中可能存在依赖于其他bean 的属性,进行递归初始化
populateBean(beanName, mbd, instanceWrapper);
// 还记得 init-method 吗?还有 InitializingBean 接口?还有 BeanPostProcessor 接口?
// 这里就是处理 bean 初始化完成后的各种回调
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
......
return exposedObject;
}
我们这里主要分析循环引用,忽略其他逻辑
-
createBeanInstance(beanName, mbd, args)
通过 BeanDefinition 获取构造函数去真正创建 bean 实例,并包装成 BeanWrapper; -
earlySingletonExposure
是否需要提前曝光,即是否需要把这个 bean 应用半成品的逻辑;单例 & 允许循环依赖 & 当前 bean 正在创建中; -
addSingletonFactory
维护半成品工厂和半成品Map,半成品工厂获取 bean 通过回调函数getEarlyBeanReference
生成,这里默认返回上面创建的 bean,也可以通过实现SmartInstantiationAwareBeanPostProcessor
接口进行维护;
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
-
populateBean
对创建的 bean 进行初始化,对 bean 进行填充,将各个属性值注入,其中可能存在依赖于其他bean 的属性(例如@Autowired),最终通过调用getBean
方法进行递归创建;
小结一下:这里首先去创建 bean,然后判断是否需要经过半成品 -> 成品的流程,需要的话构造一个ObjectFactory
放入singletonFactories
,供后面通过缓存获取 bean 时使用(doGetBean中getSingleton(beanName))。
实例分析
循环引用-2.png可以运行上面示例代码,在下面几个地方加上条件断点(beanName.equals("classRoom") || beanName.equals("student")
)进行debug
AbstractBeanFactory#doGetBean - Object sharedInstance = getSingleton(beanName);
AbstractBeanFactory#doGetBean - sharedInstance = getSingleton(beanName, () -> {...});
AbstractAutowireCapableBeanFactory#doCreateBean - if (earlySingletonExposure)
DefaultSingletonBeanRegistry#getSingleton - afterSingletonCreation(beanName);
DefaultSingletonBeanRegistry#getSingleton - addSingleton(beanName, singletonObject);
总结
- Spring 默认支持单例 bean 的循环引用,也可以通过设置
context.setAllowCircularReferences(true)
关闭循环引用; - Spring 通过3个Map1个Set来解决循环引用的问题,大致思路就如下:
1.
在加载 bean 之前先放到singletonsCurrentlyInCreation
中表示该bean已经正在创建;
2.
如果允许循环引用则先构造单例泛型bean工厂 ObjectFactory<?>
放入singletonFactories
;
3.
然后在具体碰到循环引用时取出刚才的ObjectFactory<?>
创建循环引用的实例,然后暂存在earlySingletonObjcets
表示半成品bean,因为此时尚未进行初始化其依赖的属性尚未赋值;
4.
解决了这个循环引用的问题,这时内部被依赖的 bean就可以顺利初始化完成并加入到singletonObjcets
这个成品bean集合中(后续getBean都是直接从这里面获取);
5.
内部依赖bean构建完成继续回去完成外层bean的初始化(这时才对依赖bean的属性进行赋值),然后也放到singletonObjcets
中;
网友评论