Atomikos事务失效之配置类问题

2 篇文章 0 订阅

前言

之前搭建多数据源和分布式事务架构时,已经实际测试了确实可以实现多数据源切换抛异常后,所有事务成功回滚。但是之后在整合Shiro后,发现了原本的UserService的某个方法回滚事务失败,在切换第三个数据源进行操作抛异常后,第一和第二个数据源中的sql竟然被commit了,这让我觉得很诧异。

后面我解决BUG后,强行制造了一个空指针异常,用JRebel重新加载后,发现事务竟然又生效了!之后我又多次进行测试,发现项目启动后UserService的事务实际已经失效,其他Serivce正常,但是在修改UserService代码用JRebel重新加载后,发现事务控制又正常了。
Amazing!!难道JRebel和我项目的事务控制有什么不可告人的秘密吗?

JRebel重新加载

为此,我特意观察了JRebel重新加载的过程
在这里插入图片描述在这里插入图片描述

其中,在我们点击build后,控制台会打出Reload class...,这是在重新加载修改后的类的字节码文件,再重新放入target目录中。

当我们再次发起请求时,控制台打出

Destroying singleton for re-registering: userServiceImpl
Adding bean 'userServiceImpl'
Autowiring annotations for bean 'userController' [com.itoyoung.controller.UserController]

起初我看其日志表达的意思大概是重新加载了这个Service并且重新注入了Bean,后面我查资料发现,JRebel竟然支持动态实时地在Spring中添加bean和依赖,以及支持在其他框架中所做的各种各样的改变(小声bb: 实际使用好像也没这么牛逼,当然,也可能是我使用的姿势不对hhh),跟我当初想的差不多。也就是说,它把我修改的UserService重新加载了之后再注入到Spring容器中了.

ok…懂了!兄弟萌!把公屏打在懂了上!!!

把特…重新注入了这个Bean跟我们的事务控制重新生效又有什么关系呢?

Bean注入顺序

我又继续顺着这个思路往下思考,突然明白是不是因为我们的UserService注入到容器中的顺序有问题呢?
既然是事务控制失效,那么是不是跟我们自定义的支持分布式事务的SqlSessionTemplate有关系呢?
于时我在UsersviceImpl的构造方法中打出服务名

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements IUserService {

    public UserServiceImpl() {
        log.info("***************UserServiceImpl**************");
    }

    // 省略```
}

然后在CustomSqlSessionTemplate中打出日志

/**
* 自定义sqlSessionTemplate支持多数据源的分布式事务
*
* @return CustomSqlSessionTemplate
*/
@Bean("sqlSessionTemplate")
public CustomSqlSessionTemplate sqlSessionTemplate() {
    //构造函数中SqlSessionFactory即默认对象
    CustomSqlSessionTemplate customSqlSessionTemplate = new CustomSqlSessionTemplate(defaultSqlSessionFactory);
    customSqlSessionTemplate.setTargetSqlSessionFactorys(new HashMap<>(sqlSessionFactoryMap));
    log.info("Dynamic sqlSessionTemplate Registry");
    log.info("注册sqlSessionTemplate成功,一共注册{}个sqlSessionTemplate", sqlSessionFactoryMap.size() + 1);
    return customSqlSessionTemplate;
}

启动时观察控制台输出日志,发现UserServiceImpl的日志竟然先于CustomSqlSessionTemplate打出,那是不是意味着userServicesqlSessionTemplate先注入到Spring容器中,从而导致userService分布式事务控制失效

带着这个疑问,我尝试去控制使userServicesqlSessionTemplate之后再注入.

@ConditionalOnBean

@Configuration
@ConditionalOnBean(name = "sqlSessionTemplate")
public class ShiroConfig {
    //省略...
}

此注解只有加在shiroConfig上才不会报错,放在其他bean上都会启动失败:

Consider revisiting the entries above or defining a bean of type 'org.apache.shiro.mgt.SecurityManager' in your configuration.

这个注解的意思就是当前bean注入依赖于某个bean,存在才去加载
反之,还有一个注解@ConditionalOnMissingBean: 排斥某个bean,没有才会加载

加上后启动发现,事务控制竟然生效了!其他服务也都正常运行.
然而,就当我以为万事大吉的时候,发现登录竟然失效了,报错:

No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.  This is an invalid application configuration."

简单的说,就是既然你把我的配置加载后置,成功让你的事务控制生效了,那你就不要怪老子罢工不干了.
即ShiroConfig中所有的配置全部都加载失败,这套方案也不行
ps: 注解@DependsOn也无效

解决办法

将config配置类引入的Service该成引入Mapper或另写一个不需要事务控制的Service

@Component
public class PasswordRealm extends AuthorizingRealm {

    // @Resource
    // private IUserService userService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserPermissionsMapper userPermissionsMapper;

    // 省略...
}

这样就完美解决了本架构中的分布式事务控制失效的问题

其实这里最终的解决办法很简单,可能一开始用Mapper查询都不会出现这种问题,但是我想记录的是一个寻找解决问题的过程.
既加深自身了对技术的理解和熟悉度,也学习一些更多的东西.同时也给大家解决这类事务失效的问题提供一个小小的思路吧~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值