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 的父类型
相关推荐
ym hyd 11126 分钟前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
risc12345630 分钟前
什么是码点?
java
JavaPub-rodert1 小时前
公共 Docker 镜像源总失效?用 Harbor Proxy Cache 自建 Docker Hub 镜像缓存
java·缓存·docker
智鸟科技GemeOpen开发者智能设备1 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
自强的小白1 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务
Wang's Blog1 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生1 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入
萧瑟余晖2 小时前
Spring Boot Starter机制与自定义详解
spring boot
她的男孩2 小时前
分片上传的大文件人人可下载:文件模块 isPrivate 在合并时被抹成 false,另有 4 个静默坑
java·后端·架构
SWAGGY..2 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法