`
narcissusoyf
  • 浏览: 154117 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

阅读更多

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

<?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的代码:

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:

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定义的顺序吧:

	<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一次:

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()的时候,依赖了一个“半成品”,结果就悲剧了。

	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 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会在那个时候给你设置进去。

分享到:
评论
1 楼 javapub 2012-02-09  
Thanks, 研究的很深入嘛。 其实说Spring加载bean是没有顺序的,因为spring确实为了这个做了好多。 而你用的org.springframework.aop.framework.ProxyFactoryBean,是spring架构内的一个类,多少有点入侵spring架构的意思,多以导致了加载失败。 如果一般的bean都是自己写的,基本没有问题的。

相关推荐

    尚硅谷]_佟刚_Spring IOC 容器中 Bean 的生命周期.pdf

    1、&lt;bean&gt;标签主要用来进行Bean定义; 2、alias用于定义Bean别名的; 3、import用于导入其他配置文件的Bean定义,这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[] {“config1.xml...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.1.1. 使用BeanPostProcessor的Hello World示例 3.7.1.2. RequiredAnnotationBeanPostProcessor示例 3.7.2. 用...

    跟我学spring3(1-7)

    【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3 【第六章】 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3 【第六章】 AOP 之 6.3 基于...

    spring-training:Spring Framework简介和示例

    这会将bean定义的范围限定为每个Spring IoC容器一个实例(默认)。 原型 这将单个bean定义的范围限定为具有任意数量的对象实例。 要求 这将bean定义的范围限定为HTTP请求。 仅在可感知网络的Spring ...

    spring aop 实现源代码--xml and annotation(带lib包)

    在这里,我们分别定义了一个MessageSender对象(messageSenderImpl)和一个Before Advice对象(logBeforeAdvice),并定义了一个 org.springframework.aop.framework.ProxyFactoryBean对象(messageSender),...

    Spring.3.x企业应用开发实战(完整版).part2

    4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    跟我学spring3(1-7).pdf

    —— 5.1 概述 5.2 SpEL基础5.3 SpEL语法5.4在Bean定义中使用EL6.1 AOP基础6.2 AOP的HelloWorld6.3 基于Schema的AOP6.4 基于@AspectJ的AOP 6.5 AspectJ切入点语法详解6.6 通知参数6.7 通知顺序6.8 切面实例化模型

    跟开涛学Spring

    1.17 【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3 . . . . . . . . . . . . . . . .197 1.18 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . ....

    Spring.html

    注意:使用注解的方式,最终通知和后置通知顺序换了,建议使用环绕通知 注解 配置 声明式事务管理 PlatFormTransactionManager:平台事务管理器:定义了commit/rollback Mybatis/jdbc:...

    Spring3.x企业应用开发实战(完整版) part1

    4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...

    Spring面试题

    Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。 组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下: ☆...

    SpringSecurity 3.0.1.RELEASE.CHM

    Spring Bean配置 18.4.6. LDAP属性和自定义UserDetails 19. JSP标签库 19.1. 声明Taglib 19.2. authorize标签 19.3. authentication 标签 19.4. accesscontrollist 标签 20. Java认证和授权服务(JAAS)供应...

    Spring Security-3.0.1中文官方文档(翻译版)

    Spring Bean 配置 19.4.6. LDAP 属性和自定义UserDetails 20. JSP 标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java 认证和授权服务...

    ssh(structs,spring,hibernate)框架中的上传下载

     第3~9行定义了一个数据源,其实现类是apache的BasicDataSource,第11~25行定义了Hibernate的会话工厂,会话工厂类用Spring提供的LocalSessionFactoryBean维护,它注入了数据源和资源映射文件,此外还通过一些键值...

    Spring Security 中文教程.pdf

    Spring Bean配置 19.4.6. LDAP属性和自定义UserDetails 20. JSP标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java认证和授权服务...

    springmybatis

    其实还有更简单的方法,而且是更好的方法,使用合理描述参数和SQL语句返回值的接口(比如IUserOperation.class),这样现在就可以至此那个更简单,更安全的代码,没有容易发生的字符串文字和转换的错误.下面是详细...

    spring-learn:Spring轻松学习demo

    3.意识到弹簧-让bean获取spring容器的服务 BeanNameAware可以获取容器中bean的名称 ApplicationContextAware当前的applicationContext,这也可以调用容器的服务 4. Bean的自动装配 byName根据属性名称自动装配-set...

    java面试题

    select执行顺序? 答:from where group by having select order by Collection和Collections的区别? 答:Collection是集合类的父类,继承它的主要由set和list Collections是针对集合类的帮助类,它提供了一...

    17-IoC配置-import导入配置文件

    Spring容器中的bean定义冲突问题 同id的bean,后定义的覆盖先定义的 导入配置文件可以理解为将导入的配置文件复制粘贴到对应位置 导入配置文件的顺序与位置不同可能会导致最终程序运行结果不同

Global site tag (gtag.js) - Google Analytics