spring 循环依赖注入
2017-09-28 13:43
465 查看
什么是循环依赖
循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环。spring 中循环依赖注入分三种情况
1. 构造器循环依赖
2. setter方法循环注入
2.1 setter方法注入 单例模式(scope=singleton)
2.2 setter方法注入 非单例模式
我们首先创造3个互相依赖的bean类
A.java
public class A { private B b; public A(){} public A(B b){ this.b = b; } public B getB() { return b; } public void setB(B b) { this.b = b; } public void hello(){ b.doHello(); } public void doHello(){ System.out.println("I am A"); } }
B.java
public class B { private C c; public B(){} public B(C c){ this.c = c; } public C getC() { return c; } public void setC(C c) { this.c = c; } public void hello(){ c.doHello(); } public void doHello(){ System.out.println("I am B"); } }
C.java
public class C { private A a; public C(){} public C(A a){ this.a = a; } public A getA() { return a; } public void setA(A a) { this.a = a; } public void hello(){ a.doHello(); } public void doHello(){ System.out.println("I am C"); } }
执行类SpringMain.java
public class SpringMain { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("bean-circle.xml"); A a = A.class.cast(ac.getBean("a")); a.hello(); } }
1. 构造器循环依赖
表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。* 在创建A类时,构造器需要B类,那将去创建B,
* 在创建B类时又发现需要C类,则又去创建C,
* 最后在创建C时发现又需要A;从而形成一个环,没办法创建。
Spring容器将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。
<bean id="a" class="cn.com.infcn.test.A"> <constructor-arg ref="b" /> </bean> <bean id="b" class="cn.com.infcn.test.B"> <constructor-arg ref="c" /> </bean> <bean id="c" class="cn.com.infcn.test.C"> <constructor-arg ref="a" /> </bean>
执行SpringMain.main()方法报错
2. setter方法循环注入
setter循环依赖:表示通过setter注入方式构成的循环依赖。对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖。
2.1 setter方法注入 单例模式 (scope=”singleton”)
具体步骤如下:1. Spring容器创建单例“A” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“A” 标识符放到“当前创建Bean池”;然后进行setter注入“B”;
2. Spring容器创建单例“B” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory”用于返回一个提前暴露一个创建中的Bean,并将“B” 标识符放到“当前创建Bean池”,然后进行setter注入“C”;
3. Spring容器创建单例“C” Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“C” 标识符放到“当前创建Bean池”,然后进行setter注入“A”;进行注入“A”时由于提前暴露了“ObjectFactory”工厂从而使用它返回提前暴露一个创建中的Bean;
4. 最后在依赖注入“B”和“A”,完成setter注入。
<bean id="a" class="cn.com.infcn.test.A"> <property name="b" ref="b"></property> </bean> <bean id="b" class="cn.com.infcn.test.B"> <property name="c" ref="c"></property> </bean> <bean id="c" class="cn.com.infcn.test.C"> <property name="a" ref="a"></property> </bean>
执行SpringMain.main()方法打印如下:
I am B
2.2 非单例 setter 循环注入(scope=“prototype”)
对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。<bean id="a" class="cn.com.infcn.test.A" scope="prototype"> <property name="b" ref="b"></property> </bean> <bean id="b" class="cn.com.infcn.test.B" scope="prototype"> <property name="c" ref="c"></property> </bean> <bean id="c" class="cn.com.infcn.test.C" scope="prototype"> <property name="a" ref="a"></property> </bean>
执行SpringMain.main()方法报错
模拟 Spring 单例 setter 循环依赖实现
创建一个ObjectFactory.javapublic class ObjectFactory<T> { private String className; private T t; public ObjectFactory(String className, T t) { this.className = className; this.t = t; } public T getObject() { //如果该bean使用了代理,则返回代理后的bean,否则直接返回bean return t; } }
模拟实现类
import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class Main { // 单例Bean的缓存池 public static final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); //单例Bean在创建之初过早的暴露出去的Factory,为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了。 public static final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); //执行了工厂方法生产出来的Bean,总不能每次判断是否解决了循环依赖都要执行下工厂方法吧,故而缓存起来。 public static final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); public static void main(String[] args) { A a = (A)getA(); a.hello(); a = (A)getA(); a.hello(); B b = (B)getB(); b.hello(); C c = (C)getC(); c.hello(); } //模拟 spring中 applicationContext.getBean("a") public static Object getA(){ String beanName = "A"; Object singletonObject = getSingleton(beanName); if(singletonObject == null){ A bean = new A(); singletonFactories.put(beanName, new ObjectFactory<A>(beanName, bean)); bean.setB((B)getB()); addSingleton("A", bean); return bean; } return singletonObject; } //模拟 spring中 applicationContext.getBean("b") public static Object getB(){ String beanName = "B"; Object singletonObject = getSingleton(beanName); if(singletonObject == null){ B bean = new B(); singletonFactories.put(beanName, new ObjectFactory<B>(beanName, bean)); bean.setC((C)getC()); addSingleton(beanName, bean); return bean; } return singletonObject; } //模拟 spring中 applicationContext.getBean("c") public static Object getC(){ String beanName = "C"; Object singletonObject = getSingleton(beanName); if(singletonObject == null){ C bean = new C(); singletonFactories.put(beanName, new ObjectFactory<C>(beanName, bean)); bean.setA((A)getA()); addSingleton(beanName, bean); return bean; } return singletonObject; } public static void addSingleton(String beanName, Object singletonObject){ singletonObjects.put(beanName, singletonObject); earlySingletonObjects.remove(beanName); singletonFactories.remove(beanName); } public static Object getSingleton(String beanName){ Object singletonObject = singletonObjects.get(beanName); if(singletonObject==null){ synchronized (singletonObjects) { singletonObject = earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); earlySingletonObjects.put(beanName, singletonObject); singletonFactories.remove(beanName); } } } } return singletonObject; } }
singletonObjects:单例Bean的缓存池
singletonFactories:单例Bean在创建之初过早的暴露出去的Factory,为什么采用工厂方式,是因为有些Bean是需要被代理的,总不能把代理前的暴露出去那就毫无意义了。
earlySingletonObjects:执行了工厂方法生产出来的Bean,总不能每次判断是否解决了循环依赖都要执行下工厂方法吧,故而缓存起来。
getA()方法、 getB()方法、 getC()方法 是为了模拟applicationContext.getBean() 方法获取bean实例的。因为这里省略了xml配置文件,就把getBean() 方法拆分了三个方法。
这里的ObjectFactory有什么用呢,为什么不直接保留bean 实例对象呢?
spring源码中是这样实现的如下代码:
从源码中可以看出,这个ObjectFactory的作用是:如果bean配置了代理,则返回代理后的bean。
想了解更多精彩内容请关注我的公众号
本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8
点击这里快速进入简书
GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT
相关文章推荐
- Spring循环依赖正确性及Bean注入的顺序关系详解
- 关于spring 注入中循环 ,环形依赖问题的明确
- Spring进行初始化和销毁 依赖注入
- Spring注解开发-属性依赖注入指定名称的bean
- spring依赖注入2、注解概述
- spring循环依赖
- Spring依赖属性注入属性设置
- spring四种依赖注入方式
- 什么是Spring的依赖注入?
- Spring源码学习--Bean对象循环依赖问题解决(四)
- Spring对字段和集合的注入---依赖注入
- 使用dom4j:读取spring配置文件,实例化bean对象并注入依赖对象
- Spring的两种依赖注入方式:setter方法注入与构造方法注入
- Spring.NET依赖注入框架学习--实例化容器常用方法
- Spring IOC(三)依赖注入
- Spring依赖注入原理学习
- spring的依赖注入
- Spring【依赖注入】就是这么简单
- Spring依赖注入方式: