
作者:以奇
在软件开发领域,面向切面编程(AOP)作为一种强大的技术手段,极大地促进了代码的模块化与可维护性,尤其在处理横切关注点方面表现出色。本文将深入探讨 Java 平台上的 AOP 实现,聚焦于 Spring AOP 框架及其在实际项目中的应用限制,以团队内部广泛应用的日志框架 Diagnose 为例,揭示了 Spring AOP 在处理非 Bean 类方法、静态方法及内部调用时的局限性。
一、AOP 概述:以 Diagnose 为例
说起 AOP 的实现方式,大家可能第一时间想到的是 Spring AOP。Spring AOP 通过封装 Cglib 和 JDK 动态代理的相关逻辑,提供给我们方便的途径来生成动态代理对象,从而轻松实现方法执行前后的切面逻辑。很多常见的日志框架、权限校验框架(Apache Shiro)、RPC 调用框架(Apache Dubbo)的切面逻辑都是通过集成 Spring AOP 来实现的。
我们组内也有一个被广泛使用的日志框架:Diagnose,其相关的切面逻辑实现也是通过 Spring AOP 的方式来完成的。简而言之,使用 AOP 达到的效果是:针对那些被 @Diagnosed 注解标注的方法,在执行完之后,会将方法执行的入参,返回值,过程中的日志打印等信息都记录下来,最终将调用堆栈串联起来,展示在前端,方便问题排查和溯源。
如下图所示,当一个 Bean 对象的某个方法被 @Diagnosed 注解标注之后,一旦该方法被执行,就会在前端打印出相关的调用信息。


最终,当越来越多的方法 @Diagnosed 注解所标注,一个业务流程的调用信息就会被串联起来。
当然,Diagnose 会通过用户、诊断场景等方式来区分每条调用链路。
二、Spring AOP 的三大局限性
Diagnose 可以满足绝大多数场景,但是,使用 Spring AOP 方式实现的 Diagnose 还是存在不可避免的局限性:
1)@Diagnosed 注解所在的方法,必须是一个 Bean 对象的方法。这个很好理解,因为是通过 BeanPostProcessor 的方式,在创建 Bean 的时候进行切面逻辑的操作。如果不是一个 Bean,就无法委托给 BeanPostProcessor,也就谈不上切面了。这就导致一些非 Bean 类的方法无法被 Diagnose 记录调用信息。
2)@Diagnosed 注解所在的方法,不能是静态方法。这是因为 Spring AOP 的两种实现方式:Cglib 和 JDK 动态代理,分别是通过生成目标类的子类和实现目标接口的方式来创建动态代理的,而静态方法不能被子类重写,更谈不上接口实现。
3)@Diagnosed 注解所在的方法,必须从外部被调用才可以使切面逻辑生效,内部的 this.xxx()无法使 AOP 生效。这个是本文重点要讨论的场景。
前两个局限性很好理解,下面,我们着重针对第三个局限性进行分析。
首先来讲一下何谓“从外部被调用”。假设有以下 Bean A,他有三个方法,分别是公有方法 foo,bar 和私有方法 wof。其中 foo 方法在 A 类内部

最低0.47元/天 解锁文章
1161

被折叠的 条评论
为什么被折叠?



