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 的父类型
相关推荐
忧郁的Mr.Li11 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
yq19820430115611 小时前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class11 小时前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
有位神秘人11 小时前
kotlin与Java中的单例模式总结
java·单例模式·kotlin
golang学习记11 小时前
IntelliJ IDEA 2025.3 重磅发布:K2 模式全面接管 Kotlin —— 告别 K1,性能飙升 40%!
java·kotlin·intellij-idea
爬山算法11 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
java·压力测试·hibernate
暮色妖娆丶12 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
消失的旧时光-194312 小时前
第十四课:Redis 在后端到底扮演什么角色?——缓存模型全景图
java·redis·缓存
BD_Marathon12 小时前
设计模式——依赖倒转原则
java·开发语言·设计模式
BD_Marathon12 小时前
设计模式——里氏替换原则
java·设计模式·里氏替换原则