Springboot 事务&AOP

Springboot 事务&AOP

@Transactional事务注解

@Transactional作用:就是在当前这个方法执行开始之前来开启事务,方法执行完毕之后提交事

务。如果在这个方法执行的过程当中出现了异常,就会进行事务的回滚操作。

@Transactional注解:我们一般会在业务层当中来控制事务,因为在业务层当中,一个业务功能

可能会包含多个数据访问的操作。在业务层来控制事务,我们就可以将多个数据访问操作控制在

一个事务范围内。

  1. 异常回滚的属性:rollbackFor

  2. 事务传播行为:propagation

@Transactional //当前方法添加了事务管理
public void delete(Integer id){
	//根据部门id删除部门信息
	deptMapper.deleteById(id);
	//模拟:异常发生
	int i = 1/0;
	//删除部门下的所有员工信息
	empMapper.deleteByDeptId(id);
}

application.yml配置文件中开启事务管理日志,这样就可以在控制看到和事务相关的日志信息了

#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

rollbackFor

默认情况下,只有出现RuntimeException(运行时异常)才会回滚事务。

想让所有的异常都回滚,需要来配置@Transactional注解当中的rollbackFor属性:

rollbackFor=Exception.class

propagation

什么是事务的传播行为呢?

  • 就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

例子:

  1. 执行解散部门的业务:先删除部门,再删除部门下的员工

  2. 记录解散部门的日志,到日志表

@Transactional(rollbackFor = Exception.class)
    //通过rollbackFor这个属性可以指定出现何种异常类型回滚事务(Exception.class)
    //将当前方法交给spring进行事务管理
    // 出现任何异常回滚事物
    @Override
    public void delete(Integer id) throws Exception{
        try {
            //根据部门id删除部门信息
            deptMapper.deleteById(id);
            //模拟:异常
            int i=1/0;
            //会引发除0的算数运算异常(运行时异常)
//            if(true){
//                throw new Exception("出现异常了~~~");
//            }
            //编译时异常
            //删除部门下的所有员工信息
            empMapper.deleteByDeptId(id);
        }finally {
            //不论是否有异常,最终都要执行的代码:记录日志
            DeptLog deptLog = new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门的操作,此时解散的是" + id + "号部门");
            //调用其他业务类中的方法
            deptLogService.insert(deptLog);
        }
    }

DeptServiceImpl中的delete方法运行时,会开启一个事务。 当调用 deptLogService.insert(deptLog) 时,也会创建一个新的事务,那此时,当insert方法运行完毕之后,事务就已经提交了。

即使外部的事务出现异常,内部已经提交的事务,也不会回滚了,因为是两个独立的事务。

@Service
public class DeptLogServiceImpl implements DeptLogService {
    @Autowired
    private DeptLogMapper deptLogMapper;
    //需要一个新的事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}

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

REQUIRES_NEW :当不希望事务之间相互影响时,可以使用该传播行为。比如:下订

单前需要记录日志,不论订单保存成功与否,都需要保证日志记录能够记录成功。

AOP

在这里插入图片描述

添加依赖pom.xml

<!--AOP-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:Pointcut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

![外链图片转存失败,源站可能有防盗链机制,建议将在这里插入图片描述### 五种常见的通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning: 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行
@Around:环绕通知
Object result =joinPoint.proceed();

@Around环绕通知需要自己调用 joinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行
@Around环绕通知方法的返回值,必须指定为0bject,来接收原始方法的返回值。

@Pointcut

该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可。

@Pointcut("excution(* 包名+类名.*(..))")
public void pt(){}

@Before("pt()")

当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式, 当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候需要写:全类名.方法名()

//引用MyAspect1切面类中的切入点表达式
@Before("包名+类名..pt()")

通知顺序

在不同切面类中,默认按照切面类的类名字母排序:

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

控制通知的执行顺序:

在切面类上使用@Order注解,控制通知的执行顺序:

@Order(2) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行)

切入点表达式

execution

execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:

execution( 返回值 包名.类名.方法名(方法参数) throws 异常)

访问修饰符:可省略(比如: public、protected)

包名.类名: 可省略

throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

* :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分。

… :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数。

例如:

execution(void funnyboy.service.impl.DeptServiceImpl.delete(java.lang.Integer))
execution(* com..DeptServiceImpl.delete(java.lang.Integer))
// 使用..省略包名

execution(* com.itheima.service.*.*(..))
//常见的用法
@annotation
  • 基于注解的方式来匹配切入点方法

实现步骤:

  1. 编写自定义注解
  2. 在业务类要做为连接点的方法上添加自定义注解
  3. 编写切面类并引用,@annotation(funnyboy.anno.MyLog)
自定义注解的写法

添加元注解@Retention

SOURCE:编译生成的字节码文件中就不存在了。

CLASS:编译以后的字节码文件中,运行的时候内存中没有,默认值。

**RUNTIME:**编译以后的字节码文件中、运行时内存中,程序可以通过反射获取注解。(编译时运行)

添加元注解@Target

TYPE: 用在类,接口上

FIELD:用在成员变量上

METHOD: 用在方法上

PARAMETER:用在参数上

CONSTRUCTOR:用在构造方法上

LOCAL_VARIABLE:用在局部变量上

格式

元注解
public @interface 注解名称{
	属性列表;
}

连接点

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

  • 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型。
    在方法上

PARAMETER:用在参数上

CONSTRUCTOR:用在构造方法上

LOCAL_VARIABLE:用在局部变量上

格式

元注解
public @interface 注解名称{
	属性列表;
}

连接点

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

  • 对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型。
  • 对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值