Spring AOP 专题

AOP 使用

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

AOP 实现

java 复制代码
@Aspect
@Component
public class RecordTimeAspect {

    // 我要代理的路径类中的方法
    @Around("execution(* com.example.controller.*.*(..))")
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("记录时间");
        long start = System.currentTimeMillis();
        // 目标方法执行
        Object proceed = pjp.proceed();

        // 获取方法签名
        pjp.getSignature();

        long end = System.currentTimeMillis();
        System.out.println("方法执行耗时:" + (end - start));
        return proceed;
    }

}

AOP 的原理

  • 连接点:表示JoinPoint 被AOP控制的方法,包含方法信息
  • 通知:Advice 表示要升级的功能,也就是AOP中升级的方法
  • 切入点:PointCut 表示要匹配哪些条件
  • 切面:Aspect,通知+切入点
  • 目标对象:Target 通知所应用的对象,也就是原始类

用Service 打印命名,可以看出是代理对象还是原类。

java 复制代码
helloService.getClass().getName() // 下面输出表示是代理的。
// com.example.service.HelloService$$EnhancerBySpringCGLIB$$2a189639

通知

根据通知方法执行时机的不同,将通知类型分为以下常见的五类:

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

@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行

@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值。

Java 复制代码
@Aspect
@Component
public class RecordTimeAspect {

    // 定义切点
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pt() {
    }

    @Before("pt()")
    public void before(JoinPoint joinPoint) {
        System.out.println("开始执行方法:" + joinPoint.getSignature().getName());
    }

    @Around("pt()")
    public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("记录时间");
        long start = System.currentTimeMillis();
        // 目标方法执行
        Object proceed = pjp.proceed();

        // 获取方法签名
        pjp.getSignature();

        long end = System.currentTimeMillis();
        System.out.println("方法执行耗时:" + (end - start));
        return proceed;
    }

    @After("pt()")
    public void after(JoinPoint joinPoint) {
        System.out.println("结束执行方法:" + joinPoint.getSignature().getName());
    }

    @AfterReturning("pt()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("方法正常返回,返回结果为:" + joinPoint.getSignature().getName());
    }

    @AfterThrowing("pt()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("方法异常返回,异常结果为:" + joinPoint.getSignature().getName());
    }

}

通知顺序

如果一个方法被多个切面,切到了是都会执行的。

默认顺序按照类名的字母排序。

用 @Order(数字) 加在切面类上来控制顺序

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

大概流程是,环绕->方法运行之前,如果有多个切面,第二个切面执行,环绕->方法运行之前->方法运行结束或者方法报错 ->回到环绕...如此执行。

java 复制代码
@Aspect
@Component
@Order(3)
public class RecordTimeAspect {

切入点表达式

  • 介绍:描述切入点方法的一种表达式。
  • 作用:用来决定项目中的哪些方法需要加入通知

常见形式:

  • execution(......):根据方法的签名来匹配
  • @annotation(......) :根据注解匹配

注解方式

java 复制代码
@Before("@annotation(com.ruoyi.common.annotation.DataScope)")
public void doBefore(JoinPoint point) {

}

注解的第二种方式,可以直接获取注解

java 复制代码
@DataScope(deptAlias = "dddd")
public String hello() {

// 切面类中
@DataScope(deptAlias = "dddd")
public String hello() {

JoinPoint 类

java 复制代码
@Before("@annotation(dataScope)")
public void before(JoinPoint joinPoint, DataScope dataScope) {
    // 获取方法名
    String name = joinPoint.getSignature().getName();
    // 获取目标对象
    Object target = joinPoint.getTarget();
    // 获取目标类
    String name1 = target.getClass().getName();
    // 获取目标方法参数
    Object[] args = joinPoint.getArgs();
}

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

  • 对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint
  • 对于其它四种通知,获取连接点信息只能使用 JoinPoint ,它是 ProceedingJoinPoint 的父类型
相关推荐
vx_bisheyuange34 分钟前
基于SpringBoot的青年公寓服务平台
前端·vue.js·spring boot·毕业设计
小王不爱笑13239 分钟前
SpringBoot 项目新建的五种方式详细笔记
spring boot·笔记·后端
愤怒的代码1 小时前
从开发调试到生产上线:全维度 Android 内存监控与分析体系构建
android·java·kotlin
悟能不能悟1 小时前
java HttpServletRequest 设置header
java·开发语言
悟空码字1 小时前
SpringBoot整合FFmpeg,打造你的专属视频处理工厂
java·spring boot·后端
独自归家的兔1 小时前
Spring Boot 版本怎么选?2/3/4 深度对比 + 迁移避坑指南(含 Java 8→21 适配要点)
java·spring boot·后端
trayvontang1 小时前
Spring Gateway核心概念、流程及原理
spring·gateway·spring gateway
郝学胜-神的一滴1 小时前
线程同步:并行世界的秩序守护者
java·linux·开发语言·c++·程序人生
_loehuang_1 小时前
【mole】Mole User 开源用户中心:一站式身份认证与权限管理解决方案
spring boot·nacos·开源·用户中心·mole