先抛出问题的原因:服务端的Service 出现了spring 的循环依赖。
最近接手了一个项目,项目架构使用dubbo + spring frame 分模块进行开发。
假设有dubbo 消费端 A,服务端 B、C (B 和 C 在同一个项目),在开发过程中发现A 消费B 提供的服务,spring 事务不起作用。伪代码如下:
@Controller
class A {
@Reference
private B b;
public void a() {
b.insert(obj)
}
}
@Service
@....dubbo...Service(interfacename = '...')
class B imp IB{
@Transaction
public void insert(obj) {
dao.insert(obj);
//do other
}
}
@Service
class C imp IC{
@Autowire
private B b;
@Transaction
public void insert(obj) {
b.insert(obj)
}
}
在A 调用 B 的过程中,B 业务发生异常,但是事务并没有提交,通过多次单元测试,发现只有 A -> B 消费服务时才会出现事务失效的情况,而 C -> B 事务则正常,说明配置的事务实际上是有生效的,肯定是跟dubbo 整合的时候哪里出问题了。
思路:
1、查看dubbo 服务端中B 的对象 跟C 中注入的B 是不是同一个对象
通过跟踪代码,发现 AbstractProxyInvoker 中注入的B 的对象跟 C 中注入的不是同一个,因此肯定是Spring 初始化 Bean 的过程中,没有将动态代理之后的代理对象给dubbo 使用,而是给了一个最原始的实例。
2、跟踪Spring 在何时将 bean 的引用传给dubbo 的AbstractProxyInvoker
将断点打在下图中,对比:
证明思路1 是对的,然后往上跟踪代码,会走到 AnnotationBean 中的 postProcessAfterInitialization。
AnnotationBean 是dubbo 提供的一个BeanPostProcess,可以在bean 初始化完成之后,对bean 做一些增强。
那我们大致猜想,spring 在经过aop 代理生成之后的对象,传给dubbo 的 AnnotationBean ,然后注入到AbstractProxyInvoker,那么就跟踪下事务失效的这个类到底有没有被AOP 代理。
3、将断点打在AOP 的核心类中 AbstractAutoProxyCreator,看下为什么传一个没有增强过的对象给dubbo。
可以看出,gameInfoServiceImpl,在这里走的是红色框住的代码,即没有被Aop 增强,那就原因就是 this.earlyProxyReferences.contains(cacheKey) ,接下来要去找下 earlyProxyReferences 在什么时候add bean name 的。
通过不管的回溯代码调用链,我们看到这样一行注释:
// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware.
表明了是为了解决循环依赖,才将beanName 先缓存到 earlyProxyReferences 这个容器中 。
至此,也就找到了事务不起作用的原因,只需要解决循环依赖即可。