一、前言
开发中我们经常使用 @Transactional注解来启用Spring事务管理,但是如果使用方法不当,会遇到注解不生效该事务回滚的地方却没有回滚的问题。
总结下一般是以下几个原因:
- @Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
- 默认情况下,spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。针对这种情况,可以try catch checked异常后进行手动事务回滚。
- 数据库引擎要支持事务,如果是mysql,注意表要使用支持事务的引擎,比如InnoDB,如果是MyISAM,事务是不起作用的。
- 同一个类中, 一个no-transactional的方法去调用transactional的方法, 事务会失效。
本文主要讲第四种情况如何处理。
二、示例

事务失效示例1

事务失效示例2
执行add() 方法后数据库插入了两条数据,也就说明以上两段代码中,doAdd()方法的事务增强都不会执行,具体原因可以参考这篇文章。
那么如果想让doAdd()方法在发生异常时数据库事务回滚,有什么解决办法吗?
第一步开启配置expose-proxy
如果使用的xml配置文件的方式,则添加如下内容:
<aop:aspectj-autoproxy expose-proxy="true" />
如果用的SpringBoot框架,则直接在启动类上添加如下注解即可。
@EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)
第二步修改代码
将原来的调用代码this.doAdd(person); 改为 ((PersonService)AopContext.currentProxy()).doAdd(person);

enter description here
再进行测试发现数据库只插入了一条数据(person2),说明doAdd() 方法里的事务增强生效了。
三、源码分析
点进 @EnableAspectJAutoProxy 注解代码如下:
1@Target(ElementType.TYPE) 2@Retention(RetentionPolicy.RUNTIME) 3@Documented 4@Import(AspectJAutoProxyRegistrar.class) 5public @interface EnableAspectJAutoProxy { 6 7 boolean proxyTargetClass() default false; 8 9 boolean exposeProxy() default false; 10 11}
发现它注入了一个Bean AspectJAutoProxyRegistrar,再看看这个AspectJAutoProxyRegistrar是什么。
1class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { 2 3 public void registerBeanDefinitions( 4 AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 5 6 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 7 // 解析注解EnableAspectJAutoProxy 8 AnnotationAttributes enableAspectJAutoProxy = 9 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); 10 if (enableAspectJAutoProxy != null) { 11 if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { 12 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 13 } 14 if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { 15 // 如果设置了exposeProxy=true就强制使用 16 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); 17 } 18 } 19 } 20 21}
AopConfigUtils#forceAutoProxyCreatorToExposeProxy
1// 强制使用的过程其实也是一个属性设置的过程 2 public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) { 3 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { 4 BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); 5 definition.getPropertyValues().add("exposeProxy", Boolean.TRUE); 6 } 7 } 8 9 /** 10 * The bean name of the internally managed auto-proxy creator. 11 */ 12 public static final String AUTO_PROXY_CREATOR_BEAN_NAME = 13 "org.springframework.aop.config.internalAutoProxyCreator";