http://narcissusoyf.iteye.com/blog/705511

在使用Ibatis的时候,如果某个sql的定义出现在引用sql的定义之后的话,笨笨的ibatis是会报错的。。这让用惯了spring的人会感到烦躁,为什么ibatis不能和spring一样,做到xml定义的时候与顺序无关。。。但是 spring 真的能够做到完全与bean定义的顺序无关么?下面的代码,会让我们警醒下:

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
  9. default-autowire="byName">
  10. <bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
  11. <property name="target" ref="targetA" />
  12. <property name="interceptorNames">
  13. <list>
  14. <value>beforeAdvisor</value>
  15. </list>
  16. </property>
  17. </bean>
  18. <bean id="targetA" class="reference.A"></bean>
  19. <bean id="targetB" class="reference.B"></bean>
  20. <bean id="beforeAdvice" class="reference.BeforeAdvice" />
  21. <bean id="beforeAdvisor" class="reference.BeforeAdvisor">
  22. <property name="advice" ref="beforeAdvice"/>
  23. </bean>
  24. <bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
  25. <property name="target" ref="targetB" />
  26. <property name="interceptorNames">
  27. <list>
  28. <value>beforeAdvisor</value>
  29. </list>
  30. </property>
  31. </bean>
  32. </beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-autowire="byName">
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetA" class="reference.A"></bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
</beans>

一个简单的循环引用 + ProxyFactoryBean 定义。

启动spring的代码:

Java代码  
  1. public class Main
  2. {
  3. public static void main(String[] args) throws IOException
  4. {
  5. ApplicationContext ac = new ClassPathXmlApplicationContext("/files/reference.xml");
  6. InterfaceA a = (InterfaceA) ac.getBean("a");
  7. a.ok();
  8. }
  9. }
public class Main
{
public static void main(String[] args) throws IOException
{
ApplicationContext ac = new ClassPathXmlApplicationContext("/files/reference.xml");
InterfaceA a = (InterfaceA) ac.getBean("a");
a.ok();
}
}

接着就是悲剧的error:

Java代码  
  1. Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': org.springframework.beans.factory.FactoryBeanNotInitializedException: Cannot determine target class for proxy
  2. at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:147)
  3. at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:109)
  4. at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1387)
  5. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:244)
  6. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
  7. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:1085)
  8. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1035)
  9. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
  10. ... 39 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': org.springframework.beans.factory.FactoryBeanNotInitializedException: Cannot determine target class for proxy
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:147)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:109)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1387)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:244)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:1085)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1035)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
... 39 more

好,那么我们换下bean定义的顺序吧:

Xml代码  
  1. <bean id="targetA" class="reference.A"></bean>
  2. <bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
  3. <property name="target" ref="targetA" />
  4. <property name="interceptorNames">
  5. <list>
  6. <value>beforeAdvisor</value>
  7. </list>
  8. </property>
  9. </bean>
  10. <bean id="targetB" class="reference.B"></bean>
  11. <bean id="beforeAdvice" class="reference.BeforeAdvice" />
  12. <bean id="beforeAdvisor" class="reference.BeforeAdvisor">
  13. <property name="advice" ref="beforeAdvice"/>
  14. </bean>
  15. <bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
  16. <property name="target" ref="targetB" />
  17. <property name="interceptorNames">
  18. <list>
  19. <value>beforeAdvisor</value>
  20. </list>
  21. </property>
  22. </bean>
   <bean id="targetA" class="reference.A"></bean>
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>

再run一次:

Java代码  
  1. 2010-7-4 23:09:09 org.springframework.context.support.AbstractApplicationContext prepareRefresh
  2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@32fb4f: startup date [Sun Jul 04 23:09:09 CST 2010]; root of context hierarchy
  3. 2010-7-4 23:09:09 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
  4. 信息: Loading XML bean definitions from class path resource [files/reference.xml]
  5. 2010-7-4 23:09:09 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
  6. 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d7ad1c: defining beans [targetA,a,targetB,beforeAdvice,beforeAdvisor,b]; root of factory hierarchy
  7. xxx
  8. $Proxy1
2010-7-4 23:09:09 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@32fb4f: startup date [Sun Jul 04 23:09:09 CST 2010]; root of context hierarchy
2010-7-4 23:09:09 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [files/reference.xml]
2010-7-4 23:09:09 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d7ad1c: defining beans [targetA,a,targetB,beforeAdvice,beforeAdvisor,b]; root of factory hierarchy
xxx
$Proxy1

成功了。。。。。

duo xi die ? 这是为什么呢?

这里的问题确实是出在bean定义的顺序上面。applicationContext 在refresh的时候,是按照bean定义的顺序来加载singleton bean 的(除开BeanPostProcessor,BeanPostProcessor 在singleton加载之前被bean化)。如果仅仅只是加载普通的Bean或者普通的FactoryBean的话,Spring已经通过某种方式很好的解决了singleton循环依赖的问题。

导致这个问题的原因,是由2个原因叠加产生的:

1  FactoryBean的getObject(),需要开发者自己去做处理。如果是new 个对象,那么这个对象就享受不到spring 的IOC 的好处,因为它脱离了spring 容器的管辖。 而ProxyFactoryBean 的 getObject() 在获取代理对象的时候,会对target的属性有依赖,如果target的某些值为空,会抛错。(这里的target 通指 targetName,target)

2 spring 再 bean化 bean的时候,是分为2步的,第一步可以简单的认为new个对象出来,第二步为这个new出来的对象设置属性。就是去容器里面把这个bean需要的其他bean给注入进来。在第2步的时候,注入给其他的bean的bean 可能没有被完全bean化,很有可能只是完成了第一步的bean,还是个“半成品”。但是在整个容器初始化结束的时候,这些“半成品”bean会被变成“合格品”。

1 + 2 ,恩,就是ProxyFactoryBean在getObject()的时候,依赖了一个“半成品”,结果就悲剧了。

Java代码  
  1. private synchronized Object getSingletonInstance() {
  2. if (this.singletonInstance == null) {
  3. this.targetSource = freshTargetSource();
  4. if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
  5. // Rely on AOP infrastructure to tell us what interfaces to proxy.
  6. Class targetClass = getTargetClass();
  7. if (targetClass == null) {
  8. throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
  9. }
  10. setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
  11. }
  12. // Initialize the shared singleton instance.
  13. super.setFrozen(this.freezeProxy);
  14. this.singletonInstance = getProxy(createAopProxy());
  15. }
  16. return this.singletonInstance;
  17. }
 private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}

这里的targetSource 是ProxyFactoryBean 在设置target属性的时候被设置进去的,很可惜的是,这个时候的 target 指向的Bean 还在创建中,无法走到调用setTarget() 这一步。所以,这个时候的targetSource 就是个 EMPTY_TARGET_SOURCE,它返回的targetClass  就是null. 所以就失败了。。。。

原因找到了,那么怎么解决呢?

解决也有2个方法,就是针对导致问题的2个原因作出处理么?

A方案:

针对 ProxyFactoryBean 在getObject的时候,一定要求 targetSource的targetClass不为空,那么我就让他fresh下吧。

Xml代码  
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
  7. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
  8. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
  9. default-autowire="byName">
  10. <bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
  11. <property name="targetName" value="targetA" />
  12. <property name="interceptorNames">
  13. <list>
  14. <value>beforeAdvisor</value>
  15. </list>
  16. </property>
  17. </bean>
  18. <bean id="targetA" class="reference.A"></bean>
  19. <bean id="targetB" class="reference.B"></bean>
  20. <bean id="beforeAdvice" class="reference.BeforeAdvice" />
  21. <bean id="beforeAdvisor" class="reference.BeforeAdvisor">
  22. <property name="advice" ref="beforeAdvice"/>
  23. </bean>
  24. <bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
  25. <property name="targetName" value="targetB" />
  26. <property name="interceptorNames">
  27. <list>
  28. <value>beforeAdvisor</value>
  29. </list>
  30. </property>
  31. </bean>
  32. </beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-autowire="byName">
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetA" class="reference.A"></bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
</beans>

把 target,换成targetName 就可以了

B方案:

因为spring 在解决循环依赖的时候,化了个好大好大的圈,以至于我们需要的某个属性迟迟还没有set进来。

那么我们可以把这个大大的圈分成N个小小的圈吗。。

既然spring初始化singleton bean是从xml文件第一个bean开始的(除开BeanPostProcessor),那么我们就好好利用下这个第一个Bean么。。就把bean定义的位置给换下吧。。

spring 还是和bean定义的顺序有关的。希望大家在使用spring的时候,有所警醒。这个几率的问题不是很大,在使用ProxyFactoryBean 的时候最好使用 targetName 属性。另外,在使用FactoryBean的时候,需要借鉴下ProxyFactoryBean所导致的问题,对于某些属性依赖的话,不要太相信spring会在那个时候给你设置进去。

spring的bean定义真的和顺序无关?相关推荐

  1. 品Spring:bean定义上梁山

    技术不枯燥,先来点闲聊 先说点好事高兴一下.前段时间看新闻说,我国正式的空间站建设已在进行当中.下半年,长征五号B运载火箭将在海南文昌航天发射场择机将空间站核心舱发射升空.预计用2到3年将空间站建好. ...

  2. Spring的bean定义 2 : 通用bean定义逻辑 -- AbstractBeanDefinition

    概述 AbstractBeanDefinition是最终全功能BeanDefinition实现类的基类,也就是这些类的共同属性和公共逻辑实现. AbstractBeanDefinition中并没有太复 ...

  3. Spring中Bean定义的注册流程

    目录 一.前言 二.Bean定义的注册流程 1.注册流程 2.SpringApplication组件 2.1.run()核心逻辑

  4. 厉害了,Spring中bean的12种定义方法!

    前言 在庞大的java体系中,spring有着举足轻重的地位,它给每位开发者带来了极大的便利和惊喜.我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工 ...

  5. Spring Boot: Bean definition overriding

    在本文中,我将讨论棘手的Spring Boot bean定义覆盖机制. 为了使您对该主题更加清楚,让我们从小测验开始.请看下一个简单的例子. 因此,我们有2种配置,它们使用名称beanName实例化b ...

  6. 传智播客学习之Spring——装配Bean

    昨天和大家分享了Spring的起源和作用,以及它所运用到得几大模块,算是基本对Spring有了一个整体的认识,并通过了一个小实例让我们体会了以下spring,今天详细和大家分享一下装配Bean的相关问 ...

  7. 我该如何学习spring源码以及解析bean定义的注册

    如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...

  8. Spring中Bean的定义继承

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/bean-definition-inheritance.html: Bean定义继承 bean定义可 ...

  9. 零配置 之 Spring注解实现Bean定义

    转载自  零配置 之 12.3 注解实现Bean定义 --跟我学spring3 12.3  注解实现Bean定义 12.3.1  概述 前边介绍的Bean定义全是基于XML方式定义配置元数据,且在[1 ...

最新文章

  1. 在树莓派是安装并配置NTP服务
  2. WeihanLi.Npoi 1.20.0 Released
  3. TaskScheduler相关
  4. linux cache buffer区别,Linux buffer/cache异同
  5. Vscode Todo Tree插件
  6. 分数线划定(信息学奥赛一本通-T1180)
  7. 简单批处理与多道批处理
  8. 早上起床后喝的第一杯水不要喝太多
  9. c#抽取pdf文档标题(1)
  10. 《强化学习》-读书笔记-第三章 有限马尔科夫决策过程
  11. matlab 2020b linux版本 下载
  12. 【源码】基于MPPT的光伏(PV)系统仿真
  13. 发邮件向论文作者卑微求代码模板
  14. 觅伊的进化,是陌生人社交的未来吗?
  15. js获取树形JSON数据根节点到任一子节点路径
  16. 基于单片机的测温风扇控制系统设计(#0420)
  17. JAVA java学习(9)——————java常用开发工具介绍
  18. 世界级3D渲染大赛TOP3大佬们的制作流程大揭秘!
  19. 数据库系统概论(第四版)习题解答
  20. bfo java_Java 利用BFO操作PDF文件

热门文章

  1. C经典面试题之深入解析字符串拷贝的sprintf、strcpy和memcpy使用与区别
  2. 2019年第十届蓝桥杯 - 省赛 - C/C++大学B组 - C. 数列求值
  3. MapReduce不同进度的Reduce都在干什么?
  4. Python os.makedirs详细介绍
  5. 2013\Province_C_C++_A\7.错误票据
  6. 马斯克认为 AI 必超越并威胁人类,他宁愿搞脑机接口也不碰AI
  7. 互联网架构阶段 数据库读写分离 Amoeba
  8. 【Tiny4412】EMMC启动最小网络文件系统
  9. 《DLL木马进程内幕大揭秘》
  10. 每天一道LeetCode-----将字符串切分,使每个子串都是回文串,计算所有可能结果和最小切分次数