学习记录-springboot-web-AOP

1.事务管理

概述:是指一组数据库操作序列,这些操作要么全部执行成功,要么全部失败回滚,保证数据库的一致性和完整性。

事务的操作主要有三步:

  1. 开启事务(一组操作开始前,开启事务):start transaction / begin ;

  2. 提交事务(操作全部成功后,提交事务):commit ;

  3. 回滚事务(中间任何一个操作出现异常,回滚事务):rollback ;

事务的四个基本特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),通常简称为 ACID 特性。

  • 原子性:一个事务内的所有操作被视为一个不可分割的原子单元,要么全部成功执行,要么全部失败回滚。
  • 一致性:事务将数据库从一种状态转换到另一种状态,保证在操作序列中,每个事务的执行都符合系统的一致性规则,从而维护数据库的一致性。
  • 隔离性:多个事务可以并发执行,但是它们之间不能互相干扰,每个事务的操作似乎独立于其它事务的操作。
  • 持久性:当一个事务完成后,它对数据库所作的修改就应该永久保存下来,即使出现系统故障也不应该丢失。

1.1Spring事务管理

概述:在方法,类,接口上添加@Transactional注解,就可以管理事务

@Transactional注解上的属性:

        1.异常回滚的属性:rollbackFor

@Transactional注解默认情况下,只有异常是运行时异常,才会回滚事务,rollbackFor属性可以设置异常回滚的范围

@Transactional(rollbackFor=Exception.class)

则设置为所以异常都回滚事务

        2.事务传播行为:propagation

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

例如:两个事务方法,一个A方法,一个B方法。在这两个方法上都添加了@Transactional注解,就代表这两个方法都具有事务,而在A方法当中又去调用了B方法。

属性值含义
REQUIRED【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW需要新事务,无论有无,总是创建新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)

使用场景:

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

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

2.AOP基础

2.1 概述

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性,

原理:基于动态代理实现的,

2.2 实现步骤:

        1.导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

        2.编写AOP程序

@Component
@Aspect //当前类为切面类
@Slf4j
public class TimeAspect {

    @Around("execution(* com.*.*.*.*(..))") 
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        //记录方法执行开始时间
        long start = System.currentTimeMillis();

        //执行原始方法
        Object result = joinPoint.proceed();

        //记录方法执行结束时间
        long end = System.currentTimeMillis();

        //计算方法执行耗时
        log.info(joinPoint.getSignature()+"执行耗时: {}毫秒",end-start);

        return result;
    }
}

2.3 核心概念

  1. 切面(Aspect):切面是关注点的模块化,它包含一组相互关联的通知(Advice)和切点(Pointcut)。通知是在应用程序执行期间特定的连接点(Join Point)执行的代码,切点定义了哪些连接点会触发通知。

  2. 连接点(Join Point):连接点是在应用程序执行期间进行拦截的点,比如方法调用、异常处理等。连接点是 AOP 的基础,所有通知都与连接点相关联。

  3. 通知(Advice):通知是在特定连接点执行的代码,它包括了要在连接点执行之前、之后或周围执行的逻辑。常见的通知类型有 Before(前置通知)、After(后置通知)、Around(环绕通知)、AfterReturning(返回通知)和 AfterThrowing(异常通知)等。

  4. 切入点(Pointcut):切点是一个表达式,它定义了一组连接点,通知会被织入到这些连接点上,从而实现对应用程序的切面切入。

  5. 目标对象(Target Object):目标对象是包含连接点的原始对象。在 AOP 中,切面将会织入到目标对象中,从而改变目标对象的行为。

2.4 Spring中AOP的通知类型

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行

  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行

  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行

  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行

  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

切面类的执行顺序:

        默认按照切面类的类名字母排序,

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

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

可以通过Spring提供的@Order注解改变切面类的执行顺序

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

2.5 切入点表达式

概述:描述切入点方法的一种表达式

作用:主要用来决定项目中的哪些方法需要加入通知

1.execution(...)根据方法的签名来匹配

@Around("execution(*com.*.*.*.*(..))")
  • * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

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

切入点表达式的语法规则:

  1. 方法的访问修饰符可以省略

  2. 返回值可以使用*号代替(任意返回值类型)

  3. 包名可以使用*号代替,代表任意包(一层包使用一个*

  4. 使用..配置包名,标识此包以及此包下的所有子包

  5. 类名可以使用*号代替,标识任意类

  6. 方法名可以使用*号代替,表示任意方法

  7. 可以使用 * 配置参数,一个任意类型的参数

  8. 可以使用.. 配置参数,任意个任意类型的参数

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

2.@annotation(...)根据注解匹配

实现步骤:

  1. 编写自定义注解

  2. 在业务类要做为连接点的方法上添加自定义注解

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
}

在方法上添加注解

    @Log //自定义注解(表示:当前方法属于目标方法)
    public void list() {   
        //业务代码
    }

定义切面类,

@Component
@Aspect //标注此类为切面类
public class MyAspect6 {
    //环绕通知
    @Around("@annotation(Log注解的全类名)")
    public Object recordAround(ProceedingJoinPoint joinPoint){
       //代码实现
    }
}

连接点

@Slf4j
@Component
@Aspect
public class MyAspect {

    @Pointcut("@annotation(com.*.*.MyLog)")
    private void pt(){}
   
    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect -> before ...");
    }
    
    //后置通知
    @Before("pt()")
    public void after(JoinPoint joinPoint){
        log.info(joinPoint.getSignature().getName() + " MyAspect -> after ...");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标类名
        String name = pjp.getTarget().getClass().getName();
        log.info("目标类名:{}",name);

        //目标方法名
        String methodName = pjp.getSignature().getName();
        log.info("目标方法名:{}",methodName);

        //获取方法执行时需要的参数
        Object[] args = pjp.getArgs();
        log.info("目标方法参数:{}", Arrays.toString(args));

        //执行原始方法
        Object returnValue = pjp.proceed();

        return returnValue;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值