浅谈@Transactional与AOP

之前就在后台的dao层注意到,基本对数据库进行删除和编辑的操作都会加上@Transactional注解(如下),当时只是大概明白是进行事务回滚的。但是也不太清楚具体的执行过程,以前具体使用场景。于是搜索了相关资料来理解并记录一下。

  @Modifying
  @Transactional
  void deleteAllByDistrictId(Long districtId);

@Transactional简单概括

使用这个注解的类或者方法的事务由spring处理,即是方法里面对数据库操作,如果失败则spring负责回滚操作,成功则提交操作。也就是上周学长提到的保证事务的原子性:对数据进行操作的时候,要么全都执行,要么全都不执行。

举个例子: 当前方法需要将数据插入两个数据表:
假如第一张表插入成功了,但是插入第二张的时候失败了,怎么处理?
1.要么就保证两张表都插入成功
2.要么就进行回滚,取消对第一张表的操作。

@Transactional注解就是为了帮助我们管理这些事务。

AOP

查找资料的时候我发现这么一句话:
@Transactional原理是基于spring aop实现的。
那么Aop又是什么呢?

AOP(Aspect-oriented programming):面向切面編程。
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

与之相对是的以前常使用的OOP (Object-oriented programming)面向对象

下面用一个场景来说明一下使用Aop带给我们的好处

场景:假如说我们已经完成了一个项目,现在想加入日志功能,在save方法中打印日志。

  • 如果我们依然使用OOP面向对象,那么我们会这么做: 在save方法代码里,写上打印日志的的代码。有时候写错时甚至会导致原来执行正常的方法执行失败。
  • 如果我们使用了AOP呢?

    @Around("execution(* Controller.save(..))")
    public void print() throws Throwable {

    我们就可以使用切面,当Controller.save执行的时候,执行该打印日志的函数。
    这样的做法,对原有代码毫无入侵性,这就是AOP的好处了,把和主业务无关的事情,放到代码外面去做
    浅谈@Transactional与AOP_第1张图片

切面执行时机有五种:
「@Before」 :在目标方法调用前去通知
「@AfterReturning」 :在目标方法返回或异常后调用
「@After」 :在目标方法返回后调用
「@AfterThrowing」 :在目标方法异常后调用
「@Around」 :将目标方法封装起来,自己确定调用时机


在了解AOP是啥之后,我们再来看看实现原理:

AOP 实现主要分为两类:
1.静态 AOP
2.动态 AOP

这里我们主要讲动态 AOP ,因为spring 中 AOP 的实现是就说通过动态代理实现的

什么是代理呢,就是我再生成一个代理类,去代理Controller的save()方法,代码大概就长这样:

class ControllerProxy {
    private Controller controller;

    public void save() {
        controller.save();
        print() // 打印日志
    }
}

所以在刚才那张图中,spring实现AOP过程大概是这样的:
(这里根据不同的切面时机过程略有不同,这里是After切面)
浅谈@Transactional与AOP_第2张图片

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象。

@Transactional

@Transactional原理就是基于spring aop实现的。

根据AOP,我们就可以大概猜测出@Transactional的执行过程,这里以delete方法作为事务例子:

浅谈@Transactional与AOP_第3张图片


大概了解@Transactional执行逻辑之后,
那么我们来看看@Transactional什么时候会回滚呢?

先来看看异常的分类
浅谈@Transactional与AOP_第4张图片

一个个来说,对于加了@Transactional的方法或类:

Error: 一定会回滚

Excepion: 分为两类

  • 不可查的异常(unchecked exceptions):RuntimeException及其子类和错误(Error)
    RuntimeException的子类有很多,不只图片上的,类似的有ArithemticeExcepiton,算术运算异常等。
    需要注意的是:
    Spring框架的事务基础架构代码将默认地只在抛出error时和unchecked exceptions时才标识事务回滚
    浅谈@Transactional与AOP_第5张图片
    如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。

    @Transactional(rollbackFor = Exception.class)
  • 可查的异常(checked exceptions):Exception下除了RuntimeException外的异常,不会回滚。
    例如,SQLException, 数据库连接异常,IOException,编译异常。

对于这种IOException、SQLException等以及用户自定义的Exception异常,
JAVA编译器强制要求我们必需对出现的这些异常进行catch并处理,否则程序就不能编译通过。
所以,面对这种异常不管我们是否愿意,只能自己去写一大堆catch块去处理可能的异常。

这一点我深有体会:
浅谈@Transactional与AOP_第6张图片

上次因为写了formatter.parse(),编译器就提示必须throws ParseException,并且调用它的函数也需要throws ParseException。
浅谈@Transactional与AOP_第7张图片


文章到此结束,若有错误还请指正

你可能感兴趣的:(springboot)