Dubbo: 与spring结合使用时循环依赖导致事务失效问题

Created on 7 Aug 2018  ·  8Comments  ·  Source: apache/dubbo

  • [√] I have searched the issues of this repository and believe that this is not a duplicate.
  • [√] I have checked the FAQ of this repository and believe that this is not a duplicate.

Environment

  • Dubbo version: 2.5.7
  • Operating System version: win10
  • Java version: 1.8

Step to reproduce this issue

有A、B两个服务,相互依赖时会造成事务失效,简要代码如下:

AServiceImpl

@Service(interfaceClass=IAService.class,group="test",version="1.0.0")
@Transactional
public class AServiceImpl implements IAService {
    @Autowired
    private ITestService testService;

    @Autowired
    private IBService bService;
    @Override
    public void test(String string) {
        TestEntity testEntity = new TestEntity();
        testEntity.setTest("A-"+string);
        testEntity = testService.save(testEntity);
        if(!Objects.isNull(testEntity.getId())){
            throw new RuntimeException("AService test....a...");
        }
    }
}

BServiceImpl

@Service(interfaceClass=IBService.class,group="test",version="1.0.0")
@Transactional
public class BServiceImpl implements IBService {
    @Autowired
    private ITestService testService;

    @Autowired
    private IAService aService;//相互引用,但实际并未使用
    @Override
    public void test(String string) {
        TestEntity testEntity = new TestEntity();
        testEntity.setTest("B-"+string);
        testEntity = testService.save(testEntity);
        if(!Objects.isNull(testEntity.getId())){
            throw new RuntimeException("BService test...b....");
        }
    }
}

test

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootDubboDemoApplicationTests {

    @Autowired
    private ITestService testService;

    @Reference(group = "test", version = "1.0.0")
    private IAService iaService;
    @Reference(group = "test", version = "1.0.0")
    private IBService ibService;

    @Test
    public void  testDubbo(){
        System.out.println("start......");
        try {
            iaService.test("aaaa");
        }catch (Exception e){
            e.printStackTrace();
        }
        try {
            ibService.test("bbbbb");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

Expected Result

正常执行过后,事务应该回滚,数据库中没有保存数据

Actual Result

实际结果,a服务执行,数据库中存在记录

A-aaaa 被插入数据库

当注释掉A/B的循环依赖时,结果正常,数据库中没有数据

详细代码见:https://github.com/softxiang/springboot-dubbo-demo

另有一个问题如下:
@Service(interfaceClass=IAService.class,group="test",version="1.0.0")
@Transactional
public class AServiceImpl implements IAService

此种情况定义了一个dubbo service,是否还需要spring扫描这个service 看代码情况是不需要spring再做相应操作,但看网上很多文章包括issues中依然有人提类似问题 #2093
一个服务既是dubbo服务又是本地服务时,是否只需要注册为dubbo的service即可?

@Autowired
private IBService bService;

是否会自动区分本地service调用 还是dubbo service?

Most helpful comment

这个问题会统一在 2.6.6 版本中解决。

All 8 comments

使用spring service标记服务C、D模拟上面类似情况,未出现事务失效的问题

dubbo can't support transactions.

但是A和B之间还没有调用关系,只是做了申明就失效了

如果把A、B都显式申明为dubbo service,A可以注入,B就注入不了

如:在AServiceImpl、BServiceImpl将 a b循环依赖的@Autowired改为
@Reference(group = "test", version = "1.0.0")
后,测试方法中的

@Reference(group = "test", version = "1.0.0")
    private IBService ibService;

注入为null

有其他人发现并解决了问题,希望官方能处理一下
https://blog.csdn.net/liangshf520/article/details/79621345

有其他人发现并解决了问题,希望官方能处理一下
https://blog.csdn.net/liangshf520/article/details/79621345

这个问题和你碰到的循环依赖应该是两个问题。

你的这个问题今天我看了一下,主要原因是用于处理 dubbo 注解的 com.alibaba.dubbo.config.spring.AnnotationBean 实现的是 BeanPostProcessor 接口。这种实现方式是不太正确的,原因如下:

  • 可能存在多个 BeanPostProcessor 注册,而 AnnotationBean 是其中的一个,并且位置不是最后一个,导致 dubbo 框架看到的 bean 实例和 spring 框架最终得到的 bean 实例有区别
  • 即使 AnnotationBean 是 BeanPostProcessor 链中的最后一个,在某些场景下,比如需要解决循环依赖的时候,在 BeanPostProcessor 链执行完毕之后,spring 框架会继续对 bean 做处理,导致 spring 框架最终注册的 bean 和 dubbo 看到的仍然不一样:
        // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

                 ...

        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }   
                ...

        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

所以,正确的做法应该是废弃掉 BeanPostProcessor 的做法,而是在 ApplicationListener 中开始暴露 dubbo 服务,也就是说,等 spring 组装完毕之后 (org.springframework.context.support.AbstractApplicationContext#finishRefresh),再开始暴露服务。

总结一下,对于 dubbo 要暴露的服务,在 spring 场景下,真正应该代理的类不是 service interface 是实现,而是被 spring 正确组装以及增强完毕之后的类。

这个问题会统一在 2.6.6 版本中解决。

有其他人发现并解决了问题,希望官方能处理一下
https://blog.csdn.net/liangshf520/article/details/79621345

这个问题也看了一下。的确如作者所说,在 2.5.9 和 master 上都已经修复,问题仅存在在 2.6 分支上。根本原因还是 AnnotationBean 的问题。在他的例子中,aop 使用的是 jdk 的 dynamic proxy,导致 AnnotationBean 判断 Dubbo 服务失败,没有正常暴露服务。

改成 master 上新的机制就完全没有问题

这个问题会统一在 2.6.6 版本中解决。

感谢大佬

Was this page helpful?
0 / 5 - 0 ratings