看一个最简单的CGLIB
的例子,感受一下AOP
是如何做到的?
1 | /** |
执行后的结果显示
1 | Connected to the target VM, address: '127.0.0.1:55868', transport: 'socket' |
实际上在执行execute()
的前后就各自做了自己想要的操作。其实这个就是Spring AOP
对简单的一个原型。
@Transaction的工作原理
在Spring
中TransactionInterceptor
和PlatformTransactionManager
这两个类是整个事务模块的核心,TransactionInterceptor
负责拦截方法执行,进行判断是否需要提交或者回滚事务。PlatformTransactionManager
是Spring 中的事务管理接口,真正定义了事务如何回滚和提交。我们重点研究下这两个类的源码。
TransactionInterceptor
类中的代码有很多,我简化一下逻辑,方便说明:
1 | //以下代码省略部分内容 |
其实,这里也就是因为用到了动态代理。在事务回滚这个动作的前前后后可以做自己想要的东西。这是一个非常重要的设计思想。如果我们自己要写框架,这个模式可以作为你的第一参考。
基于注解的实现机制
- 调用注解方法
- 生成代理对象 -
CglibAopProxy
调用内部类的方法DynamicAdvisedInterceptor.intercept()
TransactionInterceptor.invoke()
拦截器拦截,在目标方法执行之前创建并加入事务AbstractPlatformTransactionManager
抽象事务管理器操作数据源DataSource
提交或回滚事务
我们看了解一下它是如何仿照最上面的code
来写的?
仿照上面Demo
的第一步:
1 | public Object getProxy(@Nullable ClassLoader classLoader) { |
仿照上面Demo的第三步:
1 | private Callback[] getCallbacks(Class<?> rootClass) throws Exception { |
仿照上面Demo的第四步:
1 | protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { |
上面的几步就完成了一个动态代理的流程,就只需要真的发生调用的时候去执行动态代理类了。
哪些场景事物会失效?
- 1、只对
public
修饰方法才起作用 - 2、
@Transaction
默认检测异常为RuntimeException
及其子类 如果有其他异常需要回滚事务的需要自己手动配置,例如:@Transactional(rollbackFor = Exception.class)
- 3、确保异常没有被
try-catch{}
,catch
以后也不会回滚 - 4、检查下自己的数据库是否支持事务,如
mysql
的mylsam
- 5、
SpringBoot
项目默认已经支持事务,不用配置;其他类型项目需要在xml
中配置是否开启事务 - 6、如果在同一个类中,一个非
@Transaction
的方法调用有@Transaction
的方法不会生效,因为代理问题
这里说下在同一个类中,一个非@Transaction
的方法调用有@Transaction
的方法不会生效。如果是在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用,必须将方法放入另外一个类中,并且该类通过Spring注入。
Spring
采用动态代理(AOP
)实现对Bean
的管理和切片,它为我们的每个class
生成一个代理对象,只有在代理对象之间进行调用时,可以触发切面逻辑。
而在同一个类中,方法B调用A,调用的事元对象的方法,而不是通过代理对象,所以Spring
无法切到这次调用,也就是无法通过注解保证事务性。
参考地址
- https://blog.csdn.net/joker8023joker/article/details/103277571
- https://blog.csdn.net/Dragon_1999/article/details/93059495
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。