问题: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.t
ransaction.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实例。这个方法是我写这篇文章时想到的,-_-!!!,看来知识果真还是好好
整理呀。