日常踩坑篇—Spring事务的两个坑点

现在的程序开发基本都是“拿来主义”,有很多现成的组件可以用,但是不究其原理,不掌握组件内部的实现机制,就会出现比较有意思的场景:“我什么都没改啊!”、“我只加了一个字段而已!”,因为你已经掉进坑里了。没有完美的解决方案,每种方案都有利有弊,AOP也是一样。Spring事务是基于Spring AOP实现的,如果你对Spring AOP还不是很熟悉,可以看看笔者的另一篇文章:源码剖析篇—Spring Aop
。下面介绍Spring事务的两个坑点。

1 调用本地方法时,本地方法的Spring事务不起作用

在这里插入图片描述
这种情况下saveUserInfo上的事务是不起作用的。我们都知道Spring注入的实例和手动new出来的实例是不一样的。Spring注入的是加工过的一个代理实例,所以在调用方法的时候不会直接进入到方法里,会被一个拦截器DynamicAdvisedInterceptor拦截。
在这里插入图片描述
在这里会获取到方法的所有intercepts,如果你在方法上加了@Transactional注解,那么事务的拦截器就会加入到intercepts集合里。所以跨实例调用,Spring事务是没有任何问题的。那为什么调用本地方法事务就会失效呢?要搞明白这个问题首先要思考一下这是一个什么样的实例。我们能明确的是调用方法和被调用方法都在同一个实例里面,不管有没有用this,都是当前实例。那这个实例具有什么样的特性呢?下面我们看一段代码:
在这里插入图片描述
如果方法加了拦截链,在这里会调用ReflectiveMethodInvocation.proceed()方法
在这里插入图片描述
如果拦截器都执行完了,会调用invokeJoinpoint()方法。
在这里插入图片描述
在这里插入图片描述
到这里应该就不需要再往下看了,最终是通过反射调用被代理实例的方法,这里的target就是被代理的实例,也就是UserServiceImpl的实例。此时就比较清晰明了了,UserServiceImpl的实例不是代理实例,没有被Spring加工过,所以不会被Spring拦截,自然调用本地方法就不会走到事务的拦截器里。

2 事务里加锁,锁可能会失效

在这里插入图片描述
这种情况下,锁可能会失效。下面我们看下Spring事务的拦截器是怎么实现的:
在这里插入图片描述
TransactionInterceptor继承了TransactionAspectSupport类,实现了MethodInterceptor接口。
在这里插入图片描述
从代码中可以看出Spring事务采用的是环绕机制,环绕是把整个方法包裹住的,如下图
在这里插入图片描述
所以当方法执行结束了,锁也释放了,事务可能还没有提交,也就是还没有调用commitTransactionAfterReturning方法。此时如果再进来一个线程,就会获取到锁访问数据库,这就相当于锁是失效的。

猜你喜欢

转载自blog.csdn.net/weixin_45497155/article/details/105917045