SSM框架从入门到入土(AOP面向切面编程)

SSM 框架从入门到入土:AOP 面向切面编程(全链路讲解)

AOP(Aspect Oriented Programming)是 Spring 框架的第二大核心(第一是 IoC),也是 SSM 项目中最常用、最能体现框架价值的部分之一。

很多人学 AOP 学到“晕”,其实核心就一句话:

散落在业务代码各处公共逻辑(日志、事务、权限、异常处理、性能监控、接口限流……)抽取出来,写成独立的模块,然后在合适的位置自动织入,业务代码只管写核心逻辑。

下面从零基础 → 能写 → 能优化 → 能讲原理的完整路径带你吃透 AOP。

1. 先搞清楚 AOP 到底在解决什么问题(业务痛点)

没有 AOP 的原始写法(你一定会这样写):

@Service
public class OrderService {

    public void createOrder(Order order) {
        // 1. 记录日志
        log.info("开始创建订单,用户ID:{}", order.getUserId());

        // 2. 权限校验
        if (!permissionService.hasPermission("order:create")) {
            throw new PermissionDeniedException();
        }

        try {
            // 3. 开启事务
            transaction.begin();

            // 核心业务逻辑
            orderDao.insert(order);
            userDao.updateBalance(order.getUserId(), order.getAmount());

            // 4. 提交事务
            transaction.commit();

            // 5. 记录成功日志
            log.info("订单创建成功,订单号:{}", order.getOrderNo());
        } catch (Exception e) {
            // 6. 回滚事务
            transaction.rollback();
            // 7. 记录异常日志
            log.error("订单创建失败", e);
            throw e;
        }

        // 8. 性能监控
        long end = System.currentTimeMillis();
        monitor.record("createOrder", end - start);
    }
}

问题

  • 业务方法里塞满了与核心逻辑无关的代码
  • 重复代码到处都是,维护噩梦
  • 想统一改日志格式?要改几十上百个地方

用了 AOP 之后(理想状态):

@Service
public class OrderService {
    @Transactional
    @Loggable("创建订单")
    @Permission("order:create")
    @Monitor("createOrder")
    public void createOrder(Order order) {
        // 只剩核心业务逻辑
        orderDao.insert(order);
        userDao.updateBalance(order.getUserId(), order.getAmount());
    }
}

AOP 帮你把日志、事务、权限、监控等横切关注点(Cross-Cutting Concerns)剥离出去。

2. AOP 核心概念(背下来就能讲)

概念中文解释对应代码例子
Aspect切面封装横切逻辑的模块(类)日志切面、事务切面
Join Point连接点程序执行过程中可以插入切面的点(Spring AOP 只支持方法方法调用、方法执行、异常抛出
Pointcut切点决定在哪些连接点上执行通知的规则(表达式)execution(* com.xxx.service..(..))
Advice通知/增强具体要执行的代码,以及执行时机@Before、@After、@Around 等
Weaving织入把切面应用到目标对象的过程(编译期/类加载期/运行期)Spring AOP 是运行期动态代理
Target目标对象被增强的原始对象OrderService
Proxy代理对象容器最终给用户使用的对象(包含增强逻辑)动态生成的代理类

3. Spring AOP 支持的 5 种通知类型(最重要)

通知类型注解执行时机是否能控制目标方法执行是否能修改返回值是否能捕获异常
前置通知@Before目标方法之前
后置通知@After目标方法之后(无论成功失败)
返回通知@AfterReturning目标方法正常返回后可以获取返回值
异常通知@AfterThrowing目标方法抛出异常后可以捕获异常
环绕通知@Around包围目标方法(最强大)

@Around 是万能的,其他四种通知最终都可以用 @Around 实现。

4. 切点表达式(Pointcut Expression)最常用写法(背 8 条就够)

// 最常用:匹配任意包下任意类的任意方法
execution(* *..*.*(..))

// 指定包
execution(* com.xxx.service.*.*(..))           // service 包下所有类所有方法
execution(* com.xxx.service..*.*(..))          // service 包及其子包

// 指定返回类型
execution(public * com.xxx..*.*(..))           // 返回 public 方法
execution(* com.xxx..*.add*(..))               // 方法名以 add 开头

// 参数匹配
execution(* add(..))                           // 任意参数
execution(* add(String, ..))                   // 第一个参数 String,其余任意
execution(* add(String, int))                  // 精确匹配两个参数

// 常用组合
@annotation(com.xxx.annotation.Loggable)       // 带有某个注解的方法
within(com.xxx.service.*)                      // 类型匹配
bean(orderService)                             // bean 名称匹配

小技巧@annotation 是项目中最常用的方式(自定义注解 + AOP)

5. 快速上手:三种常用实现方式对比

方式配置量主流程度推荐场景
XML 配置老项目、必须统一管理
@AspectJ 注解★★★★★绝大多数现代项目
纯 Java 配置(@Bean)零配置、模块化强

最推荐:@AspectJ 注解方式(Spring 官方也主推)

@Aspect
@Component
public class LogAspect {

    // 定义切点(可复用)
    @Pointcut("@annotation(com.xxx.annotation.Loggable)")
    public void logPointcut() {}

    @Around("logPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();

        // 获取方法签名、参数等
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();

        log.info("开始执行方法:{},参数:{}", methodName, Arrays.toString(args));

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

            long cost = System.currentTimeMillis() - start;
            log.info("方法 {} 执行完成,耗时 {}ms,返回:{}", methodName, cost, result);

            return result;
        } catch (Throwable e) {
            log.error("方法 {} 执行异常", methodName, e);
            throw e;
        }
    }
}

6. AOP 底层原理(面试必问)

Spring AOP 基于动态代理实现,主要两种:

  1. JDK 动态代理(默认,如果目标类实现了接口)
  • 必须实现接口
  • 生成的代理类实现同一接口
  1. CGLIB 动态代理(如果没有接口,或强制使用)
  • 通过继承目标类实现
  • final 类、final 方法无法代理

Spring 4.x 之后默认策略

  • 如果目标类实现了接口 → 用 JDK 动态代理
  • 否则 → 用 CGLIB

Spring Boot 2.x+ 默认全用 CGLIB(更通用)

核心流程

  1. 容器启动 → 解析 @Aspect 类
  2. 根据 Pointcut 找到所有匹配的 Join Point
  3. 创建代理对象(Proxy)
  4. 调用时 → 代理对象先执行 Advice → 再调用目标方法(或不调用)

7. SSM 项目中最常见的 AOP 应用场景(直接抄)

  1. 统一日志(操作日志、接口日志、慢查询日志)
  2. 统一异常处理(@ControllerAdvice + AOP)
  3. 事务管理(@Transactional 就是 AOP 实现的)
  4. 权限控制(自定义 @Permission + AOP)
  5. 接口限流(令牌桶、滑动窗口)
  6. 方法耗时监控 + 告警
  7. 数据脱敏(返回前对手机号、身份证脱敏)
  8. 重复提交防止(幂等性)
  9. 缓存切面(@Cacheable/@CacheEvict 也是 AOP)

8. 从入门到入土的进阶 checklist

  • [ ] 会写 @Aspect + 5 种通知
  • [ ] 能手写常用切点表达式
  • [ ] 能用 @Around 实现任意增强
  • [ ] 知道 @Order / Ordered 控制切面执行顺序
  • [ ] 能解释 JoinPoint / ProceedingJoinPoint 区别
  • [ ] 明白 JDK vs CGLIB 区别和选择依据
  • [ ] 能手写一个统一日志 + 异常处理的切面
  • [ ] 知道 AOP 性能开销(环绕通知最重)
  • [ ] 能说出 AOP 不能代理的情况(内部调用、静态方法、final 方法)

如果你能把上面 checklist 全打勾,AOP 基本就“入土”了。

想直接上手哪一块?
比如:

  • 统一日志切面完整代码
  • 自定义注解 + AOP 实现权限控制
  • AOP + 事务传播行为结合
  • AOP 失效的 10 种常见场景

告诉我,我直接给你代码 + 原理 + 踩坑总结!

文章已创建 4631

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部