笔者最近看书还是从网上找资料对循环依赖的问题, 大家都是解读了 Spring 解决循环依赖的想法(大都解释也不准确, 在《Spring 源码深度解析》作者也是看别人的博客说明了一下), 没有从源码的角度分析是怎么解决循环依赖的.
Spring 中对象可以配置成单例模式也可配置为原型模式(原型模式很值得一看).
Spring 中可以通过构造函数注入, setter 注入的方式来解决对象与对象间的依赖.
对象间的循环依赖只能配置单例, setter 注入的方式来解决, 其他方法就会报错, 下面我们通过源码分析一下.
一, 单例, setter 注入解决循环依赖
假如有 TestA,TestB,TestC 三个对象, 其中 TestA 依赖 TestB,TestB 依赖 TestC,TestC 依赖 TestA.
下面具体通过代码分析 Spring 是如何解决单例通过 Setter 注入的循环依赖.
在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 类中有几个集合类型的成员变量, 用来做缓存用的需要特别留意, 源码如下:
- public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
- ......
- 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);
- private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
- ......
- }
上面的代码中:
singletonsCurrentlyInCreation: 保存对象的 BeanName, 在创建对象之前就会把对象的 beanName 保存起来.
singletonFactories: 保存对象的 BeanName 和创建 bean 的工厂 AbstractAutowireCapableBeanFactory(ObjectFactory),(对象的构造函数是在这一步完成的)
earlySingletonObjects: 保存对象 BeanName 和 ObjectFactory#getObject 的对象(TestA),(TestA 还没 setter 注入), 此时可以作为对象填充依赖.
singletonObjects: 保存 BeanName 和 bean 的实例(此时对象已经完成了 setter 注入)
通过 Spring 获取 testA 对象:
通过 Spring 获取 testA 对象
- ApplicationContext factory=new ClassPathXmlApplicationContext("classpath:applicationContext-beans.xml");
- TestA testA = (TestA) factory.getBean("testA");
- System.out.println(testA);
factory.getBean 最终调用的是 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
下面看 doGetBean 源码:
- protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
- ......
- // 重点 1
- Object sharedInstance = this.getSingleton(beanName);
- ......
- if (mbd.isSingleton()) {
- // 重点 2
- sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
- @Override
- public Object getObject() throws BeansException {
- try {
- // 重点 3
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
- }
在上面源码中:
1, 重点 1:
// 重点 1 根据 beanName 试图从缓存中获取已经创建的对象, 第一次进入是肯定返回 null, 这个函数放在后面解释.
2, 重点 2:
在 // 重点 2 中才是真正创建 TestA 对象的方法, 下面是 // 重点 2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton 的源码:
- // 重点 2
- public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
- Object singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- ......
- // 重点 2-1
- beforeSingletonCreation(beanName);
- ......
- try {
- // 重点 2-2
- singletonObject = singletonFactory.getObject();
- newSingleton = true;
- }catch (IllegalStateException ex) {
- ......
- }catch (BeanCreationException ex) {
- ......
- }
- finally {
- // 重点 2-3
- afterSingletonCreation(beanName);
- }
- }
- }
在 // 重点 2-1 中 beforeSingletonCreation 方法中只做了一件事, 就是保存 beanName 到 singletonsCurrentlyInCreation(注意), 这个时候 testA 就保存在 singletonsCurrentlyInCreation 里面了, 源码如下:
- // 重点 2-1
- protected void beforeSingletonCreation(String beanName) {
- //this.singletonsCurrentlyInCreation.add(beanName)
- if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- }
在 // 重点 2-2 会调用 // 重点 3 .
3, 重点 3:
在 // 重点 3 中 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean 调用 doCreateBean,doCreateBean 方法源码:
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
- ......
- // 重点 3-1
- boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
- if (earlySingletonExposure) {
- if (logger.isDebugEnabled()) {
- logger.debug("Eagerly caching bean'" + beanName +
- "'to allow for resolving potential circular references");
- }
- // 重点 3-2
- addSingletonFactory(beanName, new ObjectFactory<Object>() {
- @Override
- public Object getObject() throws BeansException {
- // 重点 3-3
- return getEarlyBeanReference(beanName, mbd, bean);
- }
- });
- }
- ......
- }
在 // 重点 3-1 判断 testA 必须是单例, 并存在在 singletonsCurrentlyInCreation 中, 此时才会调用 // 重点 3-2 的 addSingletonFactory 方法,// 重点 3-2 的 addSingletonFactory 方法源码,
- // 重点 3-2
- protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
- Assert.notNull(singletonFactory, "Singleton factory must not be null");
- synchronized (this.singletonObjects) {
- if (!this.singletonObjects.containsKey(beanName)) {
- // 重点 3-2-1
- this.singletonFactories.put(beanName, singletonFactory);
- this.earlySingletonObjects.remove(beanName);
- this.registeredSingletons.add(beanName);
- }
- }
- }
在上面的代码中有一个 singletonFactory 的参数, 这个参数是 // 重点 3-3 调用 getEarlyBeanReference 得到的, getEarlyBeanReference 返回的就是 ObjectFactory 对象, 是完成构造方法的.
在 // 重点 3-2-1 向 singletonFactories 添加 ObjectFactory(注意), 这个时候, testA 和 testA 的 ObjectFactory 对象保存在 singletonFactories, 并移除 earlySingletonObjects(现在 earlySingletonObjects 里面并没有 testA).
执行完 // 重点 3-2, 发现 testA 依赖 TestB 对象, 此时会递归调用 getBean 获取 TestB,testB 执行步骤和上面 testA 一样, 然后 testB 依赖 TestC, 递归调用 TestC, 此时 singletonFactories 里面保存的数据如下:
- testA -> ObjectFactory(TestA)
- testB -> ObjectFactory(TestB)
- testC -> ObjectFactory(TestC)
创建 testC 过程中执行完 // 重点 3-2, 发现依赖 testA, 此时会递归调用 getBean 获取 TestA, 这时候执行到 // 重点 1, 下面开始解析 // 重点 1 Object sharedInstance = getSingleton(beanName); 方法.
4, 重点 1:
下面是调用 // 重点 1 getSingleton(beanName), 调用 getSingleton(beanName, true)的源码:
- // 重点 1
- 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) {
- // 重点 1-1
- ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- // 重点 1-2
- singletonObject = singletonFactory.getObject();
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- return (singletonObject != NULL_OBJECT ? singletonObject : null);
- }
在上面代码中 isSingletonCurrentlyInCreation 就是判断对象 (testA) 是否被保存过(在 // 重点 2-1 的时候 testA 就被保存了)
在 // 重点 1-1 中, 从 singletonFactories 缓存中获取到 ObjectFactory(TestA),
在 // 重点 1-2 并通过模板模式获取 TestA 对象, 保存在 earlySingletonObjects 缓存中, 并移除 singletonFactories 中的 testA,
此时 testC 获取到 TestA 的早期对象, 可以注入 TestA 对象, 自此 TestC 完成依赖注入, 并把 testC 保存到 singletonObjects 中.
TestC 创建完成, 返回给 testB,testB 也算是完成了创建, 然后返回给 testA, 自此循环依赖算是完成了.
总结:
单例的 setter 注入, 会先把创建 testA,testB,testC 对象的 ObjectFactory(对象工厂)保存在 singletonFactories 里面, 然后 testC 依赖 testA, 那就从 singletonFactories 缓存中拿到 testA 的 ObjectFactory, 通过 ObjectFactory 的 getObject 获取 TestA 的对象, 并保存在 earlySingletonObjects 缓存中, 清除 singletonFactories 缓存中的 testA, 此时 testC 就可以获取 earlySingletonObjects 缓存中 TestA 的对象, 完成注入 TestA 的过程. TestC 对象创建完成就可以注入到 TestB 对象中, 然后 TestB 注入到 TestA 中.
singletonsCurrentlyInCreation 就是用来保存是否试图创建某个对象的 beanName, 不管有没有创建成功, 为后来从 singletonFactories 缓存中或 earlySingletonObjects 缓存中取值做个标识.
二, 单利, 构造函数注入循环依赖
假如有 TestA,TestB 两个对象, TestA 依赖 TestB,TestB 依赖 TestA;
构造函数注入和 setter 注入的不同在于, 构造函数注入无法先调用构造函数实例化对象, 当 TestA 依赖 TestB, 会先把 testA 保存到 singletonsCurrentlyInCreation 中, 然后 getBean("testB"), 然后把 testB 保存到 singletonsCurrentlyInCreation 中, 发现 TestB 依赖 TestA, 然后再 getBean("testA"), 此时执行下面的代码(和模块一, 重点 2 是同一块代码):
- // 重点 2 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton 的源码:
- public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
- // 重点 2-0
- Object singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- ......
- // 重点 2-1
- beforeSingletonCreation(beanName);
- ......
- try {
- // 重点 2-2
- singletonObject = singletonFactory.getObject();
- newSingleton = true;
- }catch (IllegalStateException ex) {
- ......
- }catch (BeanCreationException ex) {
- ......
- }
- finally {
- // 重点 2-3
- afterSingletonCreation(beanName);
- }
- }
- }
在上面的代码中 // 重点 2-0 Object singletonObject = this.singletonObjects.get(beanName); 此时 singletonObject 为空会执行 beforeSingletonCreation 方法, 源码如下:
- // 重点 2-1
- protected void beforeSingletonCreation(String beanName) {
- //this.singletonsCurrentlyInCreation.add(beanName)
- if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- }
结果发现 singletonsCurrentlyInCreation 已经存在 testA, 抛出 BeanCurrentlyInCreationException.
setter 注入为什么不会执行这一步呢, 因为 setter 注入中会实例化 TestA,TestB 保存在缓存中, 所以在执行 // 重点 2-0 Object singletonObject = this.singletonObjects.get(beanName); 时 singletonObject 不为空, 并不会执行 beforeSingletonCreation 所以不会保存.
三, 原型模式下循环依赖
假如有 TestA,TestB 两个对象, TestA 依赖 TestB,TestB 依赖 TestA, 当调用 getBean("testA")时, 会先把 beanName(testA)保存到 isPrototypeCurrentlyInCreation 里面, 发现 TestA 依赖 TestB, 就会去 getBean("testB"), 然后把 beanName(testB)也保存到 isPrototypeCurrentlyInCreation 里面, 此时 TestB 发现依赖 TestA, 去 getBean("testA")时, 发现 isPrototypeCurrentlyInCreation 已经存在 testA, 就会抛出 BeanCurrentlyInCreationException 异常, 具体代码如下
factory.getBean 最终调用的是 org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean,
下面看 doGetBean 源码:
- protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
- ......
- if (isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
- ......
- }
- //isPrototypeCurrentlyInCreation 源码
- protected boolean isPrototypeCurrentlyInCreation(String beanName) {
- Object curVal = this.prototypesCurrentlyInCreation.get();
- return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
- }
总结技术, 也记录生活 :
不是不想哭,
而是看到灯火阑珊的时候都没有自己的家,
也觉得没法在妈妈老之前给她想要的生活,
也是不想承认却不得不努力的现实世界.
来源: https://www.cnblogs.com/java-zzl/p/10025609.html