Spring AOP

Spring AOP(Aspect-Oriented Programming)是Spring框架中的一个核心模块,旨在通过面向切面的编程方式实现业务逻辑的横切关注点。AOP允许将那些与业务逻辑无关但会在多个模块中重复出现的代码(如日志记录、事务管理、异常处理、权限校验等)从核心业务逻辑中分离出来,通过切面来集中管理,从而减少代码冗余并提高代码的可维护性。

一、Spring AOP的核心概念:

  1. Aspect(切面):切面是一个模块,它将横切关注点(如日志、事务)与核心业务逻辑分离。切面可以通过特定的注解或配置文件定义。(切点+通知)

  2. Join Point(连接点):连接点是在应用程序执行的某个点,例如方法调用或异常抛出。Spring AOP支持的方法级别的连接点。

  3. Advice(通知):通知是切面在连接点上执行的操作。Spring AOP有几种通知类型:

    • 前置通知(Before):在目标方法执行之前执行。
    • 后置通知(After):在目标方法执行之后执行。
    • 返回通知(AfterReturning):在目标方法成功返回后执行。
    • 异常通知(AfterThrowing):在目标方法抛出异常后执行。
    • 环绕通知(Around):可以在目标方法执行前后自定义行为,最灵活的通知类型。
  4. Pointcut(切入点):切入点是定义通知将会应用于哪些连接点的表达式。Pointcut是通过表达式(如正则表达式)来匹配方法或类的。

  5. Weaving(织入):织入是将切面与目标对象结合的过程。Spring AOP通过运行时动态代理实现织入,而不是在编译或类加载阶段。

  • 定义切面:通过使用@Aspect注解定义切面类。

  • 定义通知方法:在切面类中使用@Before@After@Around等注解定义通知。

  • 配置切入点表达式:通过注解或XML配置来指定切入点(即哪些方法会应用切面逻辑)。

    @Aspect
    @Component
    public class LoggingAspect {
        
        // 定义前置通知,在执行某些包下的所有方法之前执行
        @Before("execution(* com.example.service.*.*(..))")
        public void logBeforeMethod(JoinPoint joinPoint) {
            System.out.println("Method " + joinPoint.getSignature().getName() + " is called");
        }
    
        // 定义后置通知
        @After("execution(* com.example.service.*.*(..))")
        public void logAfterMethod(JoinPoint joinPoint) {
            System.out.println("Method " + joinPoint.getSignature().getName() + " has finished execution");
        }
    }
    

二、切点(Pointcut)表达式

在Spring AOP中,@annotationexecution表达式都是用于定义切点的方式。它们的功能有所不同,主要用于匹配不同的切入点(Join Point)。下面详细解释两者的区别和使用场景:

1. @annotation 表达式
  • 作用@annotation用于匹配带有特定注解的方法。也就是说,它可以匹配那些被某个注解标记的方法。
  • 适用场景:当你想拦截某些被特定注解标记的方法时,@annotation 是很好的选择。常见场景包括自定义注解,用于权限控制、日志记录等。

假设我们有一个自定义注解 @Loggable,并且希望拦截所有被这个注解标记的方法:

自定义注解

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

切面使用@annotation

@Aspect
@Component
public class LoggingAspect {

    // 使用 @annotation 切点表达式,匹配所有标记了 @Loggable 的方法
    @Before("@annotation(com.example.demo.annotation.Loggable)")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}

行效果logBefore方法会在myMethod方法调用前执行,因为myMethod@Loggable注解标记了。

2. execution 表达式
  • 作用execution用于匹配方法的执行。它可以通过方法签名来精确地指定某个类中的方法或一组方法。
  • 适用场景:当你想基于方法签名(比如方法的返回类型、包名、类名、方法名和参数类型)来拦截方法时,execution是最常用的选择。它比@annotation更加灵活,可以匹配任何方法,而不需要方法上有特定注解。
    execution(modifier-pattern? return-type-pattern declaring-type-pattern? method-name-pattern(param-pattern) throws-pattern?)
    

  • modifier-pattern:可选,方法的修饰符(如publicprotected等)。
  • return-type-pattern:返回值类型,可以使用*来表示任意返回类型。
  • declaring-type-pattern:方法所在的类或者包。
  • @annotation

    • 匹配依据:基于方法上的注解。
    • 优点:适合用在需要通过注解来控制某些横切关注点的场景,如权限检查、日志记录等。注解可以提供额外的元数据,灵活性更大。
  • execution

    • 匹配依据:基于方法签名,如返回值、类名、方法名和参数类型等。
    • 优点:适用于更广泛的场景,不需要修改现有的代码(比如添加注解),通过方法签名来进行方法的拦截。适合复杂的方法匹配。

三、通知

Spring AOP 提供了多种类型的通知(Advice),用于在不同的时机执行增强逻辑。每种通知类型都在目标方法的不同阶段进行操作。以下是主要的通知类型及其作用:

1. 前置通知(@Before)
  • 作用:在目标方法执行之前执行,适用于在调用某个方法之前执行一些预处理逻辑,比如权限检查、日志记录等。
  • 使用场景:记录方法调用的参数、检查安全权限、初始化资源等。
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
    
2. 后置通知(@After)
@After("execution(* com.example.service.*.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
    System.out.println("After method: " + joinPoint.getSignature().getName());
}

3.环绕通知(@Around)
  • 作用:最强大、最灵活的通知类型,可以在目标方法执行前后执行操作。通过ProceedingJoinPoint.proceed()来控制目标方法的执行,甚至可以完全阻止目标方法的执行。
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        System.out.println("After method: " + joinPoint.getSignature().getName());
        return result;
    }
    
    @Before在目标方法执行前参数检查、日志记录、权限检查等
    @After在目标方法执行后(无论是否抛出异常)资源清理、日志记录等
    @AfterReturning在目标方法成功返回后处理返回值、记录结果等
    @AfterThrowing在目标方法抛出异常时异常日志、异常处理、错误通知等
    @Around在目标方法执行的前后性能监控、事务管理、权限控制等

 四、切⾯优先级

在Spring AOP中,当一个目标方法匹配到多个切面时,多个切面中的通知方法都会执行,执行的顺序是由@Order注解来控制的。

@Order注解的作用:

  • @Order注解用于定义切面的执行优先级。数字越小,优先级越高,优先执行。
  • 适用于多个切面匹配到同一个方法的情况,通过@Order可以控制它们的执行顺序。
  • @Order可以加在切面类上,用来指定这个切面的优先级。

环绕通知中的执行顺序:

最终的执行顺序

  • 优先级高的环绕通知(数字小的)会在目标方法执行前最先执行前置逻辑,但它会在目标方法执行后最后执行后置逻辑
  • 也就是说,环绕通知遵循“洋葱模型”或“嵌套模型”,内层的通知执行先完成,然后再回到外层的通知。
    @Aspect
    @Component
    @Order(1) // 优先级最高
    public class FirstAspect {
        @Before("execution(* com.example.demo.service.*.*(..))")
        public void beforeAdvice(JoinPoint joinPoint) {
            System.out.println("FirstAspect before method: " + joinPoint.getSignature().getName());
        }
    
        @After("execution(* com.example.demo.service.*.*(..))")
        public void afterAdvice(JoinPoint joinPoint) {
            System.out.println("FirstAspect after method: " + joinPoint.getSignature().getName());
        }
    }
    
    @Aspect
    @Component
    @Order(2) // 优先级次之
    public class SecondAspect {
        @Before("execution(* com.example.demo.service.*.*(..))")
        public void beforeAdvice(JoinPoint joinPoint) {
            System.out.println("SecondAspect before method: " + joinPoint.getSignature().getName());
        }
    
        @After("execution(* com.example.demo.service.*.*(..))")
        public void afterAdvice(JoinPoint joinPoint) {
            System.out.println("SecondAspect after method: " + joinPoint.getSignature().getName());
        }
    }
    
  • Order(1)FirstAspect:这是优先级最高的切面。它的@Before通知会最先执行,但它的@After通知会最后执行。
  • @Order(2)SecondAspect:这是优先级第二的切面。它的@Before通知会在FirstAspect@Before之后执行,但它的@After会在FirstAspect@After之前执行。
  • SecondAspect@Before
  • FirstAspect@Before
  • 目标方法执行
  • FirstAspect@After
  • SecondAspect@After
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值