Java AOP切面顺序案例如何控序

wen java案例 22

本文目录导读:

Java AOP切面顺序案例如何控序

  1. 使用 @Order 注解(最常用)
  2. 实现 Ordered 接口
  3. 在配置类中统一管理顺序
  4. 复杂场景:同一连接点多个切面
  5. 注意点与最佳实践

在Java AOP中,控制切面(Aspect)的执行顺序是一个常见需求,我会通过几种不同的方式来说明如何控制切面顺序。

使用 @Order 注解(最常用)

示例代码

// 第一个切面 - 权限校验
@Aspect
@Component
@Order(1)  // 数值越小,优先级越高,越先执行
public class PermissionAspect {
    @Around("@annotation(permissionCheck)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, PermissionCheck permissionCheck) throws Throwable {
        System.out.println("【权限切面】开始执行 - Order 1");
        // 模拟权限校验
        String permission = permissionCheck.value();
        System.out.println("检查权限: " + permission);
        Object result = joinPoint.proceed();
        System.out.println("【权限切面】执行结束");
        return result;
    }
}
// 第二个切面 - 日志记录
@Aspect
@Component
@Order(2)  // 数值较大,优先级较低,后执行
public class LogAspect {
    @Around("@annotation(LogRecord)")
    public Object logAround(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable {
        System.out.println("【日志切面】开始执行 - Order 2");
        System.out.println("记录日志: " + logRecord.value());
        Object result = joinPoint.proceed();
        System.out.println("【日志切面】执行结束");
        return result;
    }
}
// 第三个切面 - 事务管理
@Aspect
@Component
@Order(3)  // 数值最大,最后执行
public class TransactionAspect {
    @Around("@annotation(transactional)")
    public Object manageTransaction(ProceedingJoinPoint joinPoint, Transactional transactional) throws Throwable {
        System.out.println("【事务切面】开始执行 - Order 3");
        System.out.println("开始事务");
        try {
            Object result = joinPoint.proceed();
            System.out.println("提交事务");
            return result;
        } catch (Exception e) {
            System.out.println("回滚事务");
            throw e;
        }
    }
}
// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface PermissionCheck {
    String value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface LogRecord {
    String value();
}

执行顺序说明

// 业务方法
@Service
public class UserService {
    @PermissionCheck("admin")
    @LogRecord("查询用户信息")
    @Transactional
    public User getUserById(Long id) {
        System.out.println("【业务方法】执行查询");
        return new User(id, "测试用户");
    }
}

执行顺序:

  1. 权限切面(@Order(1))先执行
  2. 日志切面(@Order(2))后执行
  3. 事务切面(@Order(3))最后执行

实现 Ordered 接口

@Aspect
@Component
public class FirstAspect implements Ordered {
    @Override
    public int getOrder() {
        return 1;  // 优先级最高
    }
    @Around("execution(* com.example.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("第一个切面执行");
        return joinPoint.proceed();
    }
}
@Aspect
@Component
public class SecondAspect implements Ordered {
    @Override
    public int getOrder() {
        return 2;
    }
    @Around("execution(* com.example.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("第二个切面执行");
        return joinPoint.proceed();
    }
}

在配置类中统一管理顺序

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    @Bean
    @Order(1)
    public PermissionAspect permissionAspect() {
        return new PermissionAspect();
    }
    @Bean
    @Order(2)
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

复杂场景:同一连接点多个切面

// 更详细的示例,展示 Around 通知的执行流程
@Aspect
@Component
@Order(1)
public class FirstAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object firstAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("【第一切面】前置逻辑 - 开始执行");
        System.out.println("【第一切面】当前时间: " + System.currentTimeMillis());
        try {
            Object result = joinPoint.proceed();
            System.out.println("【第一切面】后置逻辑 - 执行成功");
            return result;
        } catch (Exception e) {
            System.out.println("【第一切面】异常处理 - 捕捉异常");
            throw e;
        } finally {
            System.out.println("【第一切面】最终处理 - 清理资源");
        }
    }
}
@Aspect
@Component
@Order(2)
public class SecondAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object secondAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("【第二切面】前置逻辑 - 开始执行");
        System.out.println("【第二切面】方法参数: " + Arrays.toString(joinPoint.getArgs()));
        long startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("【第二切面】后置逻辑 - 方法执行耗时: " + (endTime - startTime) + "ms");
        return result;
    }
}

注意点与最佳实践

执行顺序规则

// 对于 Around 通知,执行顺序如下:
// @Order(1) 的前置逻辑
// @Order(2) 的前置逻辑
// 实际方法调用
// @Order(2) 的后置逻辑
// @Order(1) 的后置逻辑
// 形象比喻:就像层层包装
// 最外层包装(Order最小)先开始,最后结束
// 最内层包装(Order最大)后开始,先结束

最佳实践建议

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)  // 最高优先级
public class PerformanceMonitorAspect {
    // 性能监控应该在最外层
}
@Component
@Order(Ordered.LOWEST_PRECEDENCE)   // 最低优先级
public class SecurityCheckAspect {
    // 安全检查应该在最后执行
}

常见的切面顺序建议

  1. 缓存切面 - 最外层(@Order(1))
  2. 日志切面 - 第二层(@Order(2))
  3. 事务切面 - 第三层(@Order(3))
  4. 校验切面 - 最内层(@Order(4))

通过合理控制切面顺序,可以更好地组织和管理横切关注点,确保系统行为符合预期。

抱歉,评论功能暂时关闭!