http://narcissusoyf.iteye.com/blog/705511
在使用Ibatis的時(shí)候,如果某個(gè)sql的定義出現(xiàn)在引用sql的定義之后的話,笨笨的ibatis是會(huì)報(bào)錯(cuò)的。。這讓用慣了spring的人會(huì)感到煩躁,為什么ibatis不能和spring一樣,做到xml定義的時(shí)候與順序無關(guān)。。。但是 spring 真的能夠做到完全與bean定義的順序無關(guān)么?下面的代碼,會(huì)讓我們警醒下:
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>
?一個(gè)簡(jiǎn)單的循環(huán)引用 + ProxyFactoryBean 定義。
啟動(dòng)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 ? 這是為什么呢?
?
這里的問題確實(shí)是出在bean定義的順序上面。applicationContext 在refresh的時(shí)候,是按照bean定義的順序來加載singleton bean 的(除開BeanPostProcessor,BeanPostProcessor 在singleton加載之前被bean化)。如果僅僅只是加載普通的Bean或者普通的FactoryBean的話,Spring已經(jīng)通過某種方式很好的解決了singleton循環(huán)依賴的問題。
?
導(dǎo)致這個(gè)問題的原因,是由2個(gè)原因疊加產(chǎn)生的:
1??FactoryBean的getObject(),需要開發(fā)者自己去做處理。如果是new 個(gè)對(duì)象,那么這個(gè)對(duì)象就享受不到spring 的IOC 的好處,因?yàn)樗撾x了spring 容器的管轄。 而ProxyFactoryBean 的 getObject() 在獲取代理對(duì)象的時(shí)候,會(huì)對(duì)target的屬性有依賴,如果target的某些值為空,會(huì)拋錯(cuò)。(這里的target 通指 targetName,target)
?
2 spring 再 bean化 bean的時(shí)候,是分為2步的,第一步可以簡(jiǎn)單的認(rèn)為new個(gè)對(duì)象出來,第二步為這個(gè)new出來的對(duì)象設(shè)置屬性。就是去容器里面把這個(gè)bean需要的其他bean給注入進(jìn)來。在第2步的時(shí)候,注入給其他的bean的bean 可能沒有被完全bean化,很有可能只是完成了第一步的bean,還是個(gè)“半成品”。但是在整個(gè)容器初始化結(jié)束的時(shí)候,這些“半成品”bean會(huì)被變成“合格品”。
?
1 + 2 ,恩,就是ProxyFactoryBean在getObject()的時(shí)候,依賴了一個(gè)“半成品”,結(jié)果就悲劇了。
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 在設(shè)置target屬性的時(shí)候被設(shè)置進(jìn)去的,很可惜的是,這個(gè)時(shí)候的 target 指向的Bean?還在創(chuàng)建中,無法走到調(diào)用setTarget() 這一步。所以,這個(gè)時(shí)候的targetSource 就是個(gè) EMPTY_TARGET_SOURCE,它返回的targetClass? 就是null. 所以就失敗了。。。。
?
原因找到了,那么怎么解決呢?
解決也有2個(gè)方法,就是針對(duì)導(dǎo)致問題的2個(gè)原因作出處理么?
A方案:
針對(duì) ProxyFactoryBean 在getObject的時(shí)候,一定要求 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方案:
因?yàn)閟pring 在解決循環(huán)依賴的時(shí)候,化了個(gè)好大好大的圈,以至于我們需要的某個(gè)屬性遲遲還沒有set進(jìn)來。
那么我們可以把這個(gè)大大的圈分成N個(gè)小小的圈嗎。。
既然spring初始化singleton bean是從xml文件第一個(gè)bean開始的(除開BeanPostProcessor),那么我們就好好利用下這個(gè)第一個(gè)Bean么。。就把bean定義的位置給換下吧。。
?
?
spring 還是和bean定義的順序有關(guān)的。希望大家在使用spring的時(shí)候,有所警醒。這個(gè)幾率的問題不是很大,在使用ProxyFactoryBean 的時(shí)候最好使用 targetName 屬性。另外,在使用FactoryBean的時(shí)候,需要借鑒下ProxyFactoryBean所導(dǎo)致的問題,對(duì)于某些屬性依賴的話,不要太相信spring會(huì)在那個(gè)時(shí)候給你設(shè)置進(jìn)去。
?
總結(jié)
以上是生活随笔 為你收集整理的spring的bean定义真的和顺序无关? 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。