本篇将带你快速了解Spring事务管理以及面向切面编程相关知识。

一、事务

1、概述

1)事务是一组操作的集合,是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。
2)事务作用︰在数据层保障一系列的数据库操作同成功同失败
3)Spring事务作用︰在数据层或业务层保障一系列的数据库操作同成功同失败

2、事务操作

1)开启事务: start transaction / begin;

一组操作开始前,开启事务

2)提交事务: commit;

一组操作全部成功后,提交事务

3)回滚事务: rollback;

中间任何一个操作出现异常,回滚事务

3、事务管理

1)注解: @Transactional
2)位置: 业务(service)层的方法上、类上、接口上
3)作用: 将当前方法交给spring进行事务管理,方法执行前,开启事务;成功执行完毕,提交事务;出现异常,回滚事务

4)开启事务管理日志

1
2
3
4
#开启事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug

5)几点注意
Spring注解式事务通常添加在业务层接口中而不会添加到业务层实现类中,降低耦合

注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口所有方法开启事务

注:Mybatis框架使用的是JDBC事务

4、相关属性

4.1 rollbackFor

rollbackFor属性用于控制出现何种异常类型,回滚事务。

默认情况下,只有出现RuntimeException才回滚异常。

如下配置所有的异常都会回滚异常

1
2
3
4
5
6
7
8
9
@Transactional(rollbackFor = Exception.class)
@Override
public void delete(Integer id) throws Exception{
deptMapper.deteById(id); // 删除部门
if(true){ // 永远成立,会抛出如下异常(非运行时异常)
throw new Exception("出错了~");
}
empMapper.deleteByDeptId(id); // 删除部门下的员工
}

4.2 传播行为

重点掌握前两个即可

REQUIRED: 大部分情况下都是用该传播行为。

REQUIRES_NEW: 当不希望事务之间相互影响时,可以使用该传播行为。如:下订单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。

二、AOP

1、概述

AOP(Aspect Oriented Programming),面向切面编程,一种编程范式,指导开发者如何组织程序结构。

注意将我们之前学的面向对象区分开,OOP(Object oriented Programming),面向对象编程

AOP的本质:代理模式

作用:在不惊动原始设计的基础上为其进行功能增强

Spring理念:无入侵式/无侵入式

2、入门程序

以统计各个业务层方法执行耗时为例。
1)导入依赖: 在pom.xml中引入AOP依赖

1
2
3
4
5
<!-- 引入AOP依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2)编写AOP程序

AOP的应用场景:1、记录操作日志 2、权限控制 3、事务管理,等等

AOP优点:1、代码无侵入 2、减少重复代码 3、提高开发效率 4、方便维护

3、核心概念

1)连接点( JoinPoint ):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等

在SpringAOP中,理解为方法的执行

2)切入点( Pointcut ):匹配连接点的式子
在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法

一个具体方法:如 com.itweb.dao包下的BookDao接口中的无形参无返回值的save方法

匹配多个方法:如所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法

3)通知( Advice ):在切入点处执行的操作,也就是共性功能

在SpringAOP中,功能最终以方法的形式呈现

通知类:定义通知的类

4)切面(Aspect ):描述通知与切入点的对应关系

5)目标对象(Target):通知所应用的对象

6)AOP执行流程

4、进阶

4.1 AOP-通知类型

1)AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

MyBatis的动态SQL也用到了共性抽取理念。

2)5种通知类型

1、前置通知(@Before):该注解标注的通知方法在目标方法前被执行

2、后置通知(@After):此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行。类似与异常处理的 finally语句

3、环绕通知(**@Around–重点**:此注解标注的通知方法在目标方法前、后都被执行

4、返回后通知(@AfterReturning)–了解

5、抛出异常后通知(@AfterThrowing)–了解

4.2 Around

注意事项
1)环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
2)通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
3)对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
4)原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
5)由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
示例

1
2
3
4
5
6
7
@Around("pt()")
public around(ProceedingJoinPoint pjp) throws Throwable { // 抛出异常
System.out.println("around before advice...");
Object ret = pjp.proceed(); // 调用原始操作
System.out.println("around after advice...");
return ret;
}

4.3 通知顺序(了解)

1)背景

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。那么执行顺序是怎么样的?

2)顺序规则
不同切面类中,默认按照切面类的类名字母排序:

目标方法前的通知方法: 字母排名靠前的先执行
目标方法后的通知方法: 字母排名靠前的后执行

3)改变顺序
法1:手动更改类名(繁琐)
法2:**@Order(数字)**,加在切面类上来控制顺序(推荐)

目标方法前的通知方法: 数字小的先执行
目标方法后的通知方法: 数字小的后执行

4.4 常见切入点表达式

所谓切入点表达式,就是描述切入点方法的一种表达式。其主要用来决定项目中的哪些方法需要加入通知。
1)execution(最常用)
语法形式:

1
execution(访问修饰符 返回值 包名.类名.方法名(方法参数) throws 异常)

其中访问修饰符(如 public,protected),包名.类名.throws 异常,这些可以省略,但是不建议将包名.类名.省略,防止匹配范围过大而影响匹配效率。

通配符:可用于描述切入点

注:可以使用且(&&)、或(||)、非(!)来组合比较复杂的切入点表达式。

书写规范:

所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如: 查询类方法都是find 开头,更新类方法都是update开头。

描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性(当接口的实现类改变时无需改变切入点方法)

在满足业务需要的前提下,尽量缩小切入点的匹配范围,提高匹配效率。如: 包名匹配尽量使用 *匹配单个包,而不使用..。

2)**@annotation**
annotation切入点表达式,用于匹配标识有特定注解的方法

用法:@annotation(注解全类名),用法相比 execution更加灵活

1
@annotation(com.itweb.anno.Log)
1
2
3
4
@Before ("@annotation (com.itweb.anno.Log)")
public void before (){
log.info ("before ....");
}

4.5 连接点

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

对于@Around通知,获取连接点信息只能使ProceedingJoinPoint

对于其他四种通知,获取连接点信息只能使用JoinPoint ,它是 ProceedingJoinPoint的父类型