一、前言
開發中我們經常使用 @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 注解代碼如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
發現它注入了一個Bean AspectJAutoProxyRegistrar,再看看這個AspectJAutoProxyRegistrar是什么。
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 解析注解EnableAspectJAutoProxy
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
// 如果設置了exposeProxy=true就強制使用
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
AopConfigUtils#forceAutoProxyCreatorToExposeProxy
// 強制使用的過程其實也是一個屬性設置的過程
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
/** * The bean name of the internally managed auto-proxy creator. */
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";