【转】spring配置中bean的循环引用问题及解决方法
问题:Spring+Hibernate的应用中,定义了两个业务Service,这里分别称它们为serivceA,ServiceB。它们的关系简单点来说是这样的:serviceA需要引用serviceB,在serviceB中定义了一个接口列表,serverA必须在serviceB初始化时设置进列表。在纯bean的情况下,也就是这两个类不需要设置其他bean的情况下,循环引用是正常的,可以通过的。例如下面配置所表示:
<bean id="serviceA" class="A" autowire="byName" lazy-init="true"> <property name="serviceB"><ref local="serviceB"/></property> </bean> <bean id="serviceB" class="B" autowire="byName" lazy-init="true"> <property name="serviceA"><ref bean="serviceA"/></property> </bean>但是作为一个业务接口,它应该是不需要关心事务,回滚这些无关的东西,但现实又有这样的需求,所以我们必须保证透明的实现这个功能,于是引入了AOP方式解决该问题,利用的是Spring自带的org.springframework.transaction.interceptor.TransactionProxyFactoryBean.重新声明文件如下: <bean id="baseTxProxy" lazy-init="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="proxyTargetClass"><value>true</value></property> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id="serviceA" parent="baseTxProxy"> <property name="target"><ref local="serviceAImpl"/></property> </bean> <bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true"> <property name="serviceB"> <ref bean="serviceB"/> </property> </bean> <bean id="serviceB" parent="baseTxProxy" lazy-init="true"> <property name="target"><ref local="serviceBImpl"/></property> </bean> <bean id="serviceBImpl" class="D" lazy-init="true"> <property name="serviceA"> <ref bean="serviceA"/> </property> </bean>于是问题就出现了,Spring报了FactoryBeanCircularReferenceException,无法继续完成设置工作。查看TransactionProxyFactoryBean源码,其实现了FactoryBean和InitializingBean接口,应该是做了代理之后,两个代理Bean需要等待所有Bean设置完成后才会标识状态为初始化完毕,于是造成了冲突。
由于两个业务服务互相调用的路径是不相交的,所以采用了一种变通的方法,在声明serviceA时,直接定义serviceB: <bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true"> <property name="serviceB"> <bean class="B" autowire="byName"/> </property> </bean>相当于serviceB和serviceA中使用的serviceB不是同一个实例。 但是如果确实调用重合时怎么办? 解决方法是这样的: <bean id="serviceAImpl" class="serviceA" autowire="byName" lazy-init="true"> <property name="serviceB"> <ref bean="serviceBImpl"/> </property> </bean> 非常简单,serviceAImpl调用时,可能已经在事务环境中了,不需再使用serviceB代理的事务支持, 于是直接引用serviceB实例。这个方法是我写这篇文章时想到的,-_-!!!,看来知识果真还是好好 整理呀。
相关资源:敏捷开发V1.0.pptx