http://narcissusoyf.iteye.com/blog/705511
 
在使用Ibatis的時候,如果某個sql的定義出現在引用sql的定義之后的話,笨笨的ibatis是會報錯的。。這讓用慣了spring的人會感到煩躁,為什么ibatis不能和spring一樣,做到xml定義的時候與順序無關。。。但是 spring 真的能夠做到完全與bean定義的順序無關么?下面的代碼,會讓我們警醒下:
 
  Xml代碼 ?  
<? 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 >  ?? 
 
<?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代碼 ?  
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(); ?? ????} ?? }??  
 
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代碼 ?  
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??  
 
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': org.springframework.beans.factory.FactoryBeanNotInitializedException: Cannot determine target class for proxyat 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代碼 ?  
< 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 >  ?? 
 
	<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代碼 ?  
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??  
 
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代碼 ?  
private ?synchronized ?Object?getSingletonInstance()?{ ??????if ?(this .singletonInstance?==?null )?{ ?? ????????this .targetSource?=?freshTargetSource(); ?? ????????if ?(this .autodetectInterfaces?&&?getProxiedInterfaces().length?==?0  ?&&?!isProxyTargetClass())?{ ?? ?????????????? ????????????Class?targetClass?=?getTargetClass(); ?? ????????????if ?(targetClass?==?null )?{ ?? ????????????????throw ?new ?FactoryBeanNotInitializedException("Cannot?determine?target?class?for?proxy" ); ?? ????????????} ?? ????????????setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass,?this .proxyClassLoader)); ?? ????????} ?? ?????????? ????????super .setFrozen(this .freezeProxy); ?? ????????this .singletonInstance?=?getProxy(createAopProxy()); ?? ????} ?? ????return ?this .singletonInstance; ?? }??  
 
	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代碼 ?  
<? 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 >  ?? 
 
<?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定义真的和顺序无关? 的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。