Spring中解决事务以及异步注解失效

Spring中解决事务以及异步注解失效

一、重现@Transaction失效的场景

        有如下业务场景,新增订单后,自动发送短信,下面的代码在同一个类中:

@Transaction

public void addOrder(OrderInfo  orderInfo){ 

        orderMapper.insert(orderInfo);

        try{

                sendMesg(orderInfo );

        }cach(Exception e){

                e.printStrace();

        }

}

@Transaction

public void sendMesg( OrderInfo  orderInfo ){

        mesgMapper.insert(orderInfo );

        throws new RuntimeException("发送短信出现异常!");

}

        上面的伪代码模拟新增订单后,自动发送短信的业务场景。两个操作被标识为事务,为了不影响发送短信出现异常影响订单的插入,在调用发送短信的方法时,通过 try....cach...捕获其异常并处理,不影响订单表的插入。

        因为sendMesg标识为事务,其抛出异常后,事务按正常逻辑来说,事务会进行回滚,即短信表中不会插入记录。然而事与愿违,出现的结果是短信表也插入了记录。

二、重现异步注解失效的场景

1、异步注解@Async介绍

        基于@Async标注的方法,称之为异步方法,这些方法在执行的时候,spring将会为其开辟独立的线程执行,调用者无需等待它的完成,即可继续其他的操作。

2、如何使用@Async

        增加 aspectj 相关的依赖;

        修改 spring配置文件,在配置文件中增加如下配置:

        <task:annotation-driven executor = "annotationExecutor"  />

        <task:executor id="annotationExecutor" pool-size="20" />

        在方法上加上@Async注解。

3、业务场景介绍

预定旅游套餐业务场景

@Transaction 

public void planTourismPackages(Order orderInfo){

        dealPassengerTicketBusiness(orderInfo);

        dealHotelBusiness(orderInfo);

        sendMesg(orderInfo);

        sendEmial(orderInfo);

 }

@Async

public void sendMesg( OrderInfo  orderInfo ){

        mesgMapper.insert( orderInfo  );         

        throws new RuntimeException("发送短信出现异常!"); 

}

        预定旅游套餐时,首先处理机票预定业务,然后处理酒店预定业务,最后发送短信和邮件。发送短信和邮件属于辅助业务,可以让其异步执行,所以在方法上加了@Async注解,让其异步执行。为了演示效果,我将planTourismPackages方法标识为事务处理,故意让sendMesg方法抛出异常。按我们预想,因为sendMesg方法异步执行,其抛出异常应该不会影响planTourismPackages事务的提交,机票预定表和酒店预定表应该会插入记录。但是,事与愿违,sendMesg抛出异常导致planTourismPackages事务会滚了,说明@Async并未生效。

三、原因分析

        为什么会出现上面的事务和异步注解失效呢?

        spring声明式事务和异步注解的实现都是基于spring aop,即对标识的方法进行增强。spring aop的底层实现原理是 jdk 动态代理。因为事务注解方法,异步方法的调用的方法在同一个类中,所以发送短信方法是在调用方法的代理对象中执行的,没有对发送短信方法进行增强。如下图:

orderMapper.insert( orderInfo );                                                     

try{ 

         this.sendMesg( orderInfo  ); 

 }cach(Exception e){ 

        e.printStrace();   

 } 

public void planTourismPackages(Order orderInfo){                                                          

        dealHotelBusiness( orderInfo ); 

        dealPassengerTicketBusiness( orderInfo );  

        this.sendMesg( orderInfo );

        this.sendEmial( orderInfo );  

}

四、解决方案

        要解决上面的问题,必须要实现发送短信方法的增强,使其能够成为事务或者异步方法,即让代理生效。spring的解决方案如下:

        1、在spring配置文件xml新增如下语句:

        先开启cglib代理,开启 exposeProxy=true,暴露代理对象

        <aop:aspectj-autoproxy expose-proxy="true"/>

        2、使用AopContext 获取当前对象的动态代理。

        修改配置文件后,代码修改,用获取到的动态代理去执行发送短信方法,如下:

        TourServer currentProxy = (TourServer )AopContext.currentProxy();

        currentProxy.sendMesg(orderInfo);

        currentProxy.sendEmail(orderInfo);

猜你喜欢

转载自blog.csdn.net/ywl470812087/article/details/88531139