一、循环引用:

1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA。形成一个环状引用关系。

2. 代码演示样例:

CircularityA

public class CircularityA {
private CircularityB circularityB; public CircularityA() {
} public CircularityA(CircularityB circularityB) {
this.circularityB = circularityB;
} public void setCircularityB(CircularityB circularityB) {
this.circularityB = circularityB;
} public void a() {
circularityB.b();
}
}

CircularityB

public class CircularityB {
private CircularityC circularityC; public CircularityB() {
} public CircularityB(CircularityC circularityC) {
this.circularityC = circularityC;
} public void setCircularityC(CircularityC circularityC) {
this.circularityC = circularityC;
} public void b() {
circularityC.c();
}
}

CircularityC

public class CircularityC {
private CircularityA circularityA; public CircularityC() {
} public CircularityC(CircularityA circularityA) {
this.circularityA = circularityA;
} public void setCircularityC(CircularityA circularityA) {
this.circularityA = circularityA;
} public void c() {
circularityA.a();
}
}

3. Spring源代码:

在Spring源代码的AbstractAutowireCapableBeanFactory类中有例如以下代码:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
// 忽略此处代码 // Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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");
}
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
} // 下面代码忽略
}

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
return exposedObject;
}
}
}
}
return exposedObject;
}

这是Spring真正创建Bean的地方, 可是创建Bean就要考虑到处理循环引用又叫做循环依赖的问题。

代码中写到了对单例Bean循环依赖的处理。 大致就是用递归的方法找出当前Bean的所有依赖Bean, 然后所有提前缓存起来。

setter循环依赖(对于setter注入造成的依赖是通过Spring容器提前暴露刚完毕构造器注入但未完毕其它步骤(如setter注入)的Bean来完毕的,并且仅仅能解决单例作用域的Bean循环依赖)详细处理过程例如以下:

(1) Spring容器创建单例“circularityA” Bean。首先依据无參构造器创建“circularityA” Bean, 并暴露一个exposedObject用于返回提前暴露的Bean。并将“circularityA”Bean放到Catch中。然后进行setter注入“circularityB”;

(2) Spring容器创建单例“circularityB" Bean。首先依据无參构造器创建“circularityB" Bean,并暴露一个exposedObject用于返回提前暴露的Bean。并将“circularityB” Bean放到Catch中,然后进行setter注入“circularityC”;

(3) Spring容器创建单例“circularityC” Bean,首先依据无參构造器创建“circularityC” Bean,并暴露一个exposedObject用于返回暴露的Bean。并将“circularityC” Bean放入Catch中, 然后进行setter注入“circularityA”。进行注入“circularityA”时因为步骤1提前暴露了exposedObject所以从之前的catch里面拿Bean不用反复创建。

(4) 最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean。  完毕setter注入。

 

       可是对于“prototype”作用域Bean。Spring容器无法完毕依赖注入,由于“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。

另一种Spring无法解决的循环依赖方式----构造器循环依赖

如在创建CircularityA类时,构造器须要CircularityB类。那将去创建CircularityB,在创建CircularityB类时又发现须要CircularityC类,则又去创建CircularityC,终于在创建CircularityC时发现又须要CircularityA。 形成环状依赖, 从而被Spring抛出。

Spring容器将每个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此假设在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖。而对于创建完成的Bean将从“当前创建Bean池”中清除掉。

二、 循环调用



1. 定义: 循环调用事实上就是一个死循环(这个是无法解决仅仅能提前避免), 终于造成StackOverflow。

2. 工作实例:

在做Hadoop的调度中间件的时候以前出现过这一个问题, 用到的解决方法事实上和Spring的解决循环依赖的思想非常相似。 Spring用的是缓存, 我们当时用的是一个集合类。

Hadoop工作有一个常见流程: A -->  B --> C

A、B、C是必须符合前后顺序的。 可是业务系统的人可能在创建这样的顺序时建成A --> B --> C --> A形成一个环状。 那么这就是一种循环调用。

解决思想及时在建立关系时把A、B、C创建的时候就丢入集合类。 假设发现反复那么说明肯定存在某种环在里面。 然后做出对应处理。 就把循环调用提前阻止了。

Spring源代码解析 ---- 循环依赖的更多相关文章

  1. Spring源码-循环依赖源码解读

    Spring源码-循环依赖源码解读 笔者最近无论是看书还是从网上找资料,都没发现对Spring源码是怎么解决循环依赖这一问题的详解,大家都是解释了Spring解决循环依赖的想法(有的解释也不准确,在& ...

  2. 面试必杀技,讲一讲Spring中的循环依赖

    本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configu ...

  3. Spring源代码解析

    Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的启动:http://www.itey ...

  4. Spring源代码解析(收藏)

    Spring源代码解析(收藏)   Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339 Spring源代码解析(二):IoC容器在Web容器中的 ...

  5. Spring中的循环依赖解决详解

    前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文 ...

  6. Spring 如何解决循环依赖问题?

    在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的. 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能 ...

  7. Spring如何解决循环依赖问题

    目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...

  8. Spring 如何解决循环依赖的问题

    Spring 如何解决循环依赖的问题 https://blog.csdn.net/qq_36381855/article/details/79752689 Spring IOC 容器源码分析 - 循环 ...

  9. Spring如何解决循环依赖?

    介绍 先说一下什么是循环依赖,Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 ...

随机推荐

  1. jQuery+Ajax+Jsp做二级级联

    终于弄懂了这个级联,我去!必须得在博客记下来. 1, JS代码: $(document).ready(function(){ $("#select1").change(functi ...

  2. Ecmall系统自带的分页功能使用

    在控制器如果没有定义相关模型,直接使用sql语句的话,直接使用如下语句. 即: public $db; $this->db = &db(); //然后开始使用分页类 $sql='sele ...

  3. NSNumber与NSInteger的区别

    Objective-C 支持的类型有两种:基本类型 和  类. 基本类型,如同C 语言中的 int 类型一样,拿来就可以直接用. 而类在使用时,必须先创建一个对象,再为对象分配空间,接着做初始化和赋值 ...

  4. Windows failed to start.界面下修复win8引导

    首先要保证 系统本身是没有问题的 不是在装机的时候出现这种情况 那么可以按照以下方法来进行 首先要在另外一台电脑上将win8刻进u盘 启动时以u盘为第一启动项启动 进入win8装机界面 点击左下角的修 ...

  5. 【剑指Offer学习】【面试题18 :树的子结构】

    题目:输入两棵二叉树A 和B.推断B 是不是A 的子结构. 二叉树结点的定义: /** * 二叉树的树结点 */ public static class BinaryTreeNode { int va ...

  6. 微软阵营稳定的好消息:.NET开源、Visual Studio 自由

    今天各个IT社区,头版头条说的是微软.NET开源了.宇宙中最好的IED–Visual Studio Community 2013将免费提供给用户的消息. <宇宙中最强大的开发环境免费了! > ...

  7. android -- 蓝牙 bluetooth (四)OPP文件传输

    在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录 ...

  8. CentOS配置防火墙

    昨天帮朋友配置CentOSserver,一開始为了方便測试直接把防火墙关了.之后便须要配置好防火墙,网上找了几个防火墙规则都有错误,后来发现是博主发帖不认真,有太多字符错误,以下是我整理的亲測可用的防 ...

  9. PHP缓存主要技术

    1.普遍缓存技术: 数据缓存:这里所说的数据缓存是指数据库查询PHP缓存机制,每次访问页面的时候,都会先检测相应的缓存数据是否存在,如果不存在,就连接数据库,得到数据,并把查询结果序列化后保存到文件中 ...

  10. AppStore安装APP发生错误解决方法

    打开网络偏好设置 高级  -> DNS ->  +  ->  114.114.114.114