Spring AOP 的基本使用

介绍

Spring AOP(面向切面编程)是 Spring 框架提供的一个重要功能,用于实现横切关注点的模块化,AOP 允许你将业务逻辑分离出横切关注点(如日志记录、事务管理等),这些横切关注点可以跨越多个类、对象,形成一个独立的模块。通过 AOP,你可以在不修改原始代码的情况下,将横切关注点应用到程序中。

概念

  • 切面(Aspect):

定义:切面是一组横切关注点的模块化单元,它包含了横切关注点以及它们在目标对象中的应用位置。

应用:切面通常以切面类的形式存在,在 Spring AOP 中,切面类使用特定的注解来标识,比如@Aspect注解,切面类中可以包含通知(Advice)和切点(Pointcut)。

  • 通知(Advice):

定义:通知是切面的具体行为,定义了在什么时候、在哪里以及如何应用切面逻辑。

应用:通知可以使用不同的注解来标识,具体取决于通知的类型。

通知类型
类型注解使用场景举例
前置通知@Before参数校验、权限检查
返回通知@AfterReturning资源释放、日志记录
异常通知@AfterThrowing事务回滚、错误处理
后置通知@After资源清理、日志记录
环绕通知@Around接口防重复提交、性能监控
  • 切点(Pointcut):

定义:切点是在应用程序中选择连接点的特定点集合,连接点可以是方法调用、方法执行、异常处理等。

应用:切点通过表达式语言定义,以指定要在哪些连接点应用通知,在 Spring AOP 中,切点可以作为切面类中的一个方法,并使用@Pointcut注解标识。

  • 连接点(Join point):

定义:连接点是在应用程序执行过程中可能与通知相关的点,在 Spring AOP 中,连接点通常是方法的执行点。

应用:连接点是AOP的基本概念,但在具体编程中不直接指定连接点,连接点是由切点和被代理的类共同确定的。

  • 织入(Weaving):

定义:织入是将切面应用到目标对象并创建新的代理对象的过程。

应用:在 Spring AOP 中,织入可以在编译时、类加载时或运行时进行,Spring AOP 通常采用运行时织入的方式,它通过动态代理实现切面逻辑的织入。在 Spring 中,织入通常通过配置来完成,无需显式编写代码。

简单案例

案例结构

其中 MethodRuntimeLogger 为自定义注解类,MethodRuntimeAspect 为自定义切面类,这个案例是获取方法或者请求的运行时长。

具体代码

记得引入 Spring AOP 的 maven 依赖哦~

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

自定义注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于标记方法,以便在运行时记录其执行时长
 * 该注解应用于方法上,以便在方法执行前后记录其运行时长
 *
 * @author b16mt
 */
@Target(ElementType.METHOD) // 用于指定该注解只能应用于方法上
@Retention(RetentionPolicy.RUNTIME) // 用于指定该注解在运行时保留
public @interface MethodRuntimeLogger {
}

除了 METHOD 外,@Target 注解还可以使用其他元素类型,如:

  • ElementType.TYPE:可以应用于类、接口、枚举等
  • ElementType.FIELD:可以应用于字段、属性
  • ElementType.PARAMETER:可以应用于方法的参数
  • ElementType.CONSTRUCTOR:可以应用于构造函数
  • ElementType.LOCAL_VARIABLE:可以应用于局部变量

@Retention 注解则可以接受以下三个枚举值:

  • RetentionPolicy.SOURCE:表示注解仅保留在源代码中,在编译时会被丢弃,意味着在编译后的 .class 文件中不会存在该注解
  • RetentionPolicy.CLASS:表示注解会被保留到编译后的 .class 文件中,但在运行时会被丢弃,意味着在运行时无法通过反射获取到该注解信息
  • RetentionPolicy.RUNTIME:表示注解会被保留到运行时,可以通过反射来获取该注解信息,因此在运行时可以处理该注解

自定义切面类

package com.xjl.sys.method.aspect;

import com.xjl.sys.method.annotation.MethodRuntimeLogger;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

/**
 * 切面类,用于记录方法的运行时长
 *
 * @author b16mt
 */
@Slf4j
@Aspect // 标识为切面类,用于定义横切关注点的模块化单元
@Component // 标识为 Spring 组件,以便在 Spring 上下文中自动扫描并加载
public class MethodRuntimeAspect {

    /**
     * 环绕通知,捕获被 MethodRuntimeLogger 注解的方法,并记录其运行时长
     *
     * @param joinPoint 切入点,用于获取目标方法的相关信息
     * @param methodRuntimeLogger MethodRuntimeLogger 注解,标记了需要记录时长的方法
     * @return 目标方法的执行结果
     * @throws Throwable 如果目标方法执行过程中发生异常,则抛出异常
     */
    @Around("@annotation(methodRuntimeLogger)") // 这是一个环绕通知,切点为methodRuntimeLogger,代表拦截包含MethodRuntimeLogger的方法
    public Object logMethodRuntime(ProceedingJoinPoint joinPoint, MethodRuntimeLogger methodRuntimeLogger) throws Throwable {
        // 记录方法执行开始时间
        long startTime = System.currentTimeMillis();

        // 执行目标方法
        Object result = joinPoint.proceed();

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

        // 计算方法执行时长
        long executionTime = endTime - startTime;

        // 打印方法签名和执行时长
        log.info(joinPoint.getSignature() + " 执行时长:" + executionTime + "ms");

        // 返回目标方法的执行结果
        return result;
    }
}

上面 @Around 是环绕通知,可以理解为环绕通知是对目标方法的一种包裹,它完全控制了目标方法的执行过程,包括了执行前后的所有逻辑(就是我可以在执行目标方法前或后去执行我想执行的其他方法,比如上面代码的记录方法执行开始时间、记录方法执行结束时间)。

同理,@Before 前置通知可以在在执行目标方法前去执行我想执行的其他方法,就比如:

上面请求接收了前端发送过来的分页查询参数,我希望对它的参数合法性进行校验,比如页码需要大于0,下面这段代码就可以使用前置通知来实现了:

// 参数校验
if (invoiceFormPageVo.getPage() == null || invoiceFormPageVo.getPage() < 1) {
    // 如果页码为null或小于1,将页码设为默认值1
    invoiceFormPageVo.setPage(1);
}
if (invoiceFormPageVo.getPageSize() == null || invoiceFormPageVo.getPageSize() < 1) {
    // 如果每页展示的最大条数为null或小于1,将其设为默认值10
    invoiceFormPageVo.setPageSize(10);
}

总结

使用 Spring AOP 去增强方法时必须要同时创建自定义的注解和切面类,自定义注解上常用的注解为:@Target() 、@Retention() ,自定义切面类上常用的注解为:@Aspect、@Component、@Around()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这小鱼在乎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值