Skip to content

什么是 AOP? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

AOP(面向切面编程)把横切关注点(事务、日志、权限、监控——所有业务都依赖但不属于任何单一业务的逻辑)从业务代码中剥离,集中到"切面"里,运行时由代理 织入。

业务价值我用三个真实落地讲:

  • 声明式事务:@Transactional 一行注解,20 行事务样板代码消失且不可能漏写。订单服务的方法里只剩业务规则——代码里业务逻辑的"信噪比"大幅提升;
  • 审计与合规:金融系统要求敏感操作全程留痕,切面拦截"调整额度、修改费率"类方法,自动记录操作人、时间、参数、结果——合规要求从"散落几百处的手工日志"变成一段可测试的切面代码,审计覆盖率从靠自觉变成 100%;
  • 统一治理:接口限流、幂等校验、trace 埋点、入参出参脱敏,全部切面化——公司级规范下沉到基础设施,业务方零成本继承。

两个必须讲的工程边界:

  1. 代理失效场景(自调用不走代理、final 方法不能增强)会让 @Transactional 静默失效——"以为有事务其实没有"是资损级 bug,我把"事务方法必须被外部调用"写进团队规约;
  2. 切面要有克制:只放"与业务无关的横切逻辑"。见过切面里写"判断用户等级返回不同折扣"——那是业务规则,藏在切面里没人能看懂全貌。切面是基础设施的位置,不是业务的藏身之处。

一句话:AOP 的本质是让业务代码只表达业务——横切关注点收编统一治理,可读性、合规性、迭代速度三赢。

🏗 系统架构师 ​

AOP(面向切面编程)把横切关注点(cross-cutting concern)从业务逻辑中抽离,按"切面"模块化织入。核心模型:

  • 切面 Aspect:横切逻辑模块(事务、日志、鉴权、监控);
  • 连接点 JoinPoint:程序执行点,Spring 里是方法执行;
  • 切入点 Pointcut:连接点的筛选表达式(execution(* com.x.service..(..))、@annotation);
  • 通知 Advice:织入时机——@Before / @After / @AfterReturning / @AfterThrowing / @Around(最强大,包裹整个执行链,ProceedingJoinPoint.proceed() 控制)。

实现原理:动态代理——有接口 JDK 代理,无接口 CGLIB;容器启动时 BeanPostProcessor 检查是否命中切点,命中则返回代理对象,调用时代理拦截器链(Interceptor Chain)依次执行各通知。

我的落地场景:声明式事务 @Transactional(最经典)、接口日志与监控埋点(Around 记 RT、错误码)、统一权限注解(自定义 @RequiresLogin 切面)、缓存注解、审计日志、防重提交(注解 + Redis 锁)。

工程红线:自调用不走代理导致注解失效(拆 Bean 或注入自身代理);切面要薄(纯横切逻辑,不放业务);切点精确(** * 全局拦截会造成启动缓慢与意外织入)。

💻 全栈开发专家 ​

AOP(面向切面编程)= 把横切关注点(日志、事务、权限、监控)从业务代码中抽离,通过动态代理在方法执行前后统一织入。

核心概念对着代码记:

java
@Aspect
@Component
public class AccessLogAspect {
    @Pointcut("execution(* com.demo.order.service..*(..))")   // 切点:切哪里
    public void pc() {}

    @Around("pc() && @annotation(timer)")                     // 通知:干什么
    public Object around(ProceedingJoinPoint pjp, Timer timer) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();                             // 执行目标方法
        } finally {
            log.info("{}#{} cost={}ms args={}",
                pjp.getTarget().getClass().getSimpleName(),
                pjp.getSignature().getName(),
                System.currentTimeMillis() - start,
                Arrays.toString(pjp.getArgs()));
        }
    }
}

使用场景:声明式事务 @Transactional(最经典)、接口日志/耗时监控、权限校验、分布式锁注解、缓存注解、防重提交——凡是"很多方法都要做同一件事"的诉求。

实现:运行时动态代理(JDK/CGLIB),Spring 容器启动时为 Bean 生成代理对象。

必踩的坑:自调用失效(this.method() 不走代理)、final/private 方法无法增强、@Transactional 默认只回滚 RuntimeException(要 rollbackFor = Exception.class)、切面执行顺序用 @Order 控制。我把这几个点写进了团队 code review checklist。