使用@Transactional需要注意的问题: @Transactional 只能应用到 public 方法 ;AOP自调用问题

使用@Transactional需要注意的问题

1、@Transactional 只能应用到 public 方法才有效

只有@Transactional 注解应用到 public 方法,才能进行事务管理。

这是因为在使用 Spring AOP 代理时,Spring 在调用在图中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

AbstractFallbackTransactionAttributeSource类:

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class<?> targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;}

这个方法会检查目标方法的修饰符是不是 public,若不是 public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。


2、避免 Spring 的 AOP 的自调用问题

在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理。

这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。因为方法的调用是通过当前对象来调用(自己调用自己),而不是通过代理对象来调用。

BookService类

package study.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import study.dao.BookDao;

@Service
public class BookService {

    @Autowired
    BookDao bookDao;

    @Transactional(propagation = Propagation.REQUIRED)
    public void checkout(String username,int id){
        bookDao.updateStock(id);
        int price = bookDao.getPrice(id);
        bookDao.updateBalance(username,price);
        System.out.println("嵌套事务 checkout: " + TransactionSynchronizationManager.getCurrentTransactionName());
//        try {
        int i=1/0;
//        }catch (Exception e){
//            System.out.println(e.getMessage());       }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updatePrice(int id,int bookPrice){
        bookDao.updatePrice(id,bookPrice);
        System.out.println("嵌套事务 updatePrice: " + TransactionSynchronizationManager.getCurrentTransactionName());

    }

//    @Transactional
    public void mult(){
        System.out.println("嵌套事务 mult: " + TransactionSynchronizationManager.getCurrentTransactionName());
        checkout("zhangsan",3);
        updatePrice(1,200);
    }
}
    @Test
    public void test03(){
        ApplicationContext context = new
                ClassPathXmlApplicationContext("ApplicationContext.xml");
        BookService bookService = context.getBean("bookService", BookService.class);
        bookService.mult();
    }

运行,控制台输出:

嵌套事务 mult: null
嵌套事务 checkout: null

java.lang.ArithmeticException: / by zero 

刷新Navicat Lite,发现都更新了

上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,使用 AspectJ 取代 Spring AOP 代理。

原因、解决:https://www.letianbiji.com/spring/spring-aop-proxy-self-call.html

https://www.huaweicloud.com/articles/7b6692fca13f7b5c026f8f2db07f5718.html 分析到:

springAOP实例 BookService$$Cglib 继承自extend BookService,

当执行到BookService$$Cglib.mult()时,

因为BookService$$Cglib没有重写checkout(),updatePrice()方法,所以会调用 super.checkout(),super.updatePrice()。但是父类的checkout(),updatePrice()方法是未增强过的,所以事务管理失效了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值