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

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>  

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

启动spring的代码:

Java代码 复制代码  收藏代码
  1. public class Main    
  2. {   
  3.     public static void main(String[] args) throws IOException   
  4.     {   
  5.   
  6.         ApplicationContext ac = new ClassPathXmlApplicationContext("/files/reference.xml");   
  7.         InterfaceA a = (InterfaceA) ac.getBean("a");   
  8.         a.ok();   
  9.     }   
  10. }  
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>  

 再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.           
  11.        
  12.     <bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">  
  13.         <property name="targetName" value="targetA" />       
  14.         <property name="interceptorNames">  
  15.             <list>  
  16.                 <value>beforeAdvisor</value>  
  17.             </list>  
  18.         </property>  
  19.     </bean>      
  20.        
  21.     <bean id="targetA" class="reference.A"></bean>     
  22.     <bean id="targetB" class="reference.B"></bean>     
  23.     <bean id="beforeAdvice" class="reference.BeforeAdvice" />  
  24.     <bean id="beforeAdvisor" class="reference.BeforeAdvisor">  
  25.         <property name="advice" ref="beforeAdvice"/>  
  26.     </bean>  
  27.     <bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">  
  28.         <property name="targetName" value="targetB" />       
  29.         <property name="interceptorNames">  
  30.             <list>  
  31.                 <value>beforeAdvisor</value>  
  32.             </list>  
  33.         </property>  
  34.     </bean>  
  35. </beans>  

 把 target,换成targetName 就可以了

 

B方案:

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

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

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

 

 

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值