问题复现
配置好事务管理器,开启事务注解启动后,我们使用 @Transactional注解 进行声明式事务控制:
@Service
@Transactional
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
再使用消费者,远程调用失败,找不到该注册的服务。
剖析原因
首先,声明式事务控制会基于JDK动态代理,创建出已经被事务增强的代理对象;
其次,Dubbo会为服务接口创建代理对象,远程调用的实际上是这个代理对象;
问题就出在这里!
Dubbo要去包扫描配置下的包中,去扫描@Service注解并为该注解下的服务接口创建代理对象;但是在此之前,@Transactional注解已经给接口做出代理对象了;
虽然我们依旧可以给代理对象创建代理对象,但是基于JDK动态代理出的代理对象,其完整类名是com.sun.proxy.$Proxy42(后两位数字不固定),显然已经不在@Service注解扫描所配置的包下了!
既然第二次没扫描到,Dubbo自然就没能发布该服务。
解决方案
既然BUG的根源是由于@Transactional改变了包名甚至类名,导致@Service没能被扫描到,那么我们不妨指定声明式事务控制采用cglib动态代理——它不会改变包名。
<!-- 开启事务注解驱动,并指定使用cglib动态代理 -->
<tx:annotation-driven proxy-target-class="true"/>
cglib虽然不改变包名,使得事务代理对象能被再次扫描到,但它却改变了类名(即服务接口名)。因此我们还需要再将它注册到Zookeeper的时候,将它的类名(即服务接口名)改回来。
@Service(interfaceClass = HelloService.class)
@Transactional
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
⭐️