[关闭]
@lzb1096101803 2016-03-18T21:09:07.000000Z 字数 2950 阅读 433

Spring循环注入

未分类


http://blog.csdn.net/toweryangtao/article/details/8216171
Spring容器循环依赖包括构造器循环依赖和setter循环依赖

循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,则它们最终反映为一个环。此处不是循环调用,循环调用是方法之间的环调用。

循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。

  1. public class CircleA {
  2. private CircleB circleB;
  3. public CircleA() {
  4. }
  5. public CircleA(CircleB circleB) {
  6. this.circleB = circleB;
  7. }
  8. public void setCircleB(CircleB circleB)
  9. {
  10. this.circleB = circleB;
  11. }
  12. public void a() {
  13. circleB.b();
  14. }
  15. }
  16. package cn.javass.spring.chapter3.bean;
  17. public class CircleB {
  18. private CircleC circleC;
  19. public CircleB() {
  20. }
  21. public CircleB(CircleC circleC) {
  22. this.circleC = circleC;
  23. }
  24. public void setCircleC(CircleC circleC)
  25. {
  26. this.circleC = circleC;
  27. }
  28. public void b() {
  29. circleC.c();
  30. }
  31. }
  32. package cn.javass.spring.chapter3.bean;
  33. public class CircleC {
  34. private CircleA circleA;
  35. public CircleC() {
  36. }
  37. public CircleC(CircleC circleC) {
  38. this.circleC = circleC;
  39. }
  40. public void setCircleC(CircleC circleC)
  41. {
  42. this.circleC = circleC;
  43. }
  44. public void b() {
  45. circleC.c();
  46. }
  47. }
  48. <bean id="circleA" class="cn.javass.spring.chapter3.bean.CircleA">
  49. <constructor-arg index="0" ref="circleB"/>
  50. </bean>
  51. <bean id="circleB" class="cn.javass.spring.chapter3.bean.CircleB">
  52. <constructor-arg index="0" ref="circleC"/>
  53. </bean>
  54. <bean id="circleC" class="cn.javass.spring.chapter3.bean.CircleC">
  55. <constructor-arg index="0" ref="circleA"/>
  56. </bean>
  57. 写段测试代码(cn.javass.spring.chapter3.CircleTest)测试一下吧:
  58. @Test(expected = BeanCurrentlyInCreationException.class)
  59. public void testCircleByConstructor() throws Throwable {
  60. try {
  61. new ClassPathXmlApplicationContext("chapter3/circleInjectByConstructor.xml");
  62. }
  63. catch (Exception e) {
  64. //因为要在创建circle3时抛出;
  65. Throwable e1 = e.getCause().getCause().getCause();
  66. throw e1;
  67. }
  68. }

一、 构造器循环依赖

  1. 1Spring容器创建“circleA Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleB”,并将“circleA 标识符放到“当前创建Bean池”;
  2. 2Spring容器创建“circleB Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleC”,并将“circleB 标识符放到“当前创建Bean池”;
  3. 3Spring容器创建“circleC Bean,首先去“当前创建Bean池”查找是否当前Bean正在创建,如果没发现,则继续准备其需要的构造器参数“circleA”,并将“circleC 标识符放到“当前创建Bean池”;
  4. 4、到此为止Spring容器要去创建“circleABean,发现该Bean 标识符在“当前创建Bean池”中,因为表示循环依赖,抛出BeanCurrentlyInCreationException

二、setter循环依赖:表示通过setter注入方式构成的循环依赖。

如下代码所示,通过提前暴露一个单例工厂方法,从而使其他Bean能引用到该Bean。

  1. addSingletonFactory(beanName, new ObjectFactory() {
  2. public Object getObject() throws BeansException {
  3. return getEarlyBeanReference(beanName, mbd, bean);
  4. }
  5. });
  1. 具体步骤如下:
  2. 1Spring容器创建单例“circleA Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleA 标识符放到“当前创建Bean池”;然后进行setter注入“circleB”;
  3. 2Spring容器创建单例“circleB Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory”用于返回一个提前暴露一个创建中的Bean,并将“circleB 标识符放到“当前创建Bean池”,然后进行setter注入“circleC”;
  4. 3Spring容器创建单例“circleC Bean,首先根据无参构造器创建Bean,并暴露一个“ObjectFactory ”用于返回一个提前暴露一个创建中的Bean,并将“circleC 标识符放到“当前创建Bean池”,然后进行setter注入“circleA”;进行注入“circleA”时由于提前暴露了“ObjectFactory”工厂从而使用它返回提前暴露一个创建中的Bean
  5. 4、最后在依赖注入“circleB”和“circleA”,完成setter注入。

对于setter注入造成的依赖是通过Spring容器提前暴露刚完成构造器注入但未完成其他步骤(如setter注入)的Bean来完成的,而且只能解决单例作用域的Bean循环依赖

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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注