这题年年考:Spring AOP 与 AspectJ 的区别,你真的答对了吗?

8 阅读6分钟

大家好,我是小米,今年31岁,一个三年写 Bug 七年修 Bug 的 Java 工程师,从10年前的 HelloWorld 写起,现在已经在各种微服务、SpringBoot、MyBatis、Redis、Kafka 的泥潭中游刃有余(或者说挣扎求生 )。

上个月刚经历完一次让我从“凉凉候选人”变成“候选人首选”的社招面试。关键时刻就是一道看似平平无奇的 Spring 面试题:

“Spring AOP 和 AspectJ AOP 有什么区别?AOP 有哪些实现方式?”

这题你是不是也在简历投出后偶尔一带而过?我以前就是——直到这次,它差点成了我社招“终结者”。

于是我决定把这次“死里逃生”的真实经历和深度复盘分享出来,不仅告诉你 AOP 到底是怎么回事、各种实现方式的优劣、源码层怎么绕的弯,还顺便讲讲我踩过哪些坑,希望能帮你在关键时刻也来一波逆风翻盘!

故事开始:一道“看似简单”的题目

故事发生在某知名互联网公司的后端社招现场。面试官看着我简历说:

“你这里写 Spring 熟练,那问个不难的:Spring AOP 和 AspectJ AOP 有啥区别?AOP 一般有哪些实现方式?”

我心里一紧: “完了,这题平常不太关注,记得都差不多吧?”

我先老实回答了:“Spring AOP 是基于代理实现的,AspectJ 是编译期织入,性能更高……”

面试官挑了下眉毛,“哦?那你能说下 Spring AOP 为什么不支持方法内部调用吗?”

我当时一愣,脑子飞速回忆项目里用过 AOP 的场景。幸好,当时我灵机一动,用真实业务场景说了出来。

没想到,这一讲就扭转了局面——面试官笑了,说我讲得非常实战,后面的问题开始放水。

于是今天,我就来好好把这个话题讲透,帮大家在社招/校招/跳槽路上少踩坑,多答对!

什么是 AOP?Spring 为什么要用 AOP?

在回答区别之前,必须搞清楚 AOP 到底是什么,为什么需要它?

本质上就是一句话:

把跟业务无关但又很重要的逻辑(比如日志、事务、安全校验)统一抽取出来,集中处理。

我们以前做法:

逻辑和日志耦合在一起,不好维护。

引入 AOP 后可以变成:

切面代码专门记录执行时间、打印日志等。这就是 AOP 的价值!

Spring AOP 和 AspectJ AOP 有什么区别?

重点来啦!面试最常考的对比题。我们来从多个维度分析:

举个栗子:Spring AOP 为什么不支持方法内部调用?

这其实是面试官常常“追问”的点。比如你写了一个类:

你以为两个方法都能被 @LogTime 拦截吗?其实并不会。

因为内部方法调用不会走 Spring AOP 的代理链!

Spring AOP 是通过代理对象来拦截方法调用的,只有外部调用代理对象的方法时才会被拦截。

而 createOrder 调用 updateStock 是 this.updateStock() ,没走代理。怎么解决?

  • 把两个方法拆到不同 Bean 中
  • 或者用 AspectJ AOP(支持编译期织入)

AOP 有哪些实现方式?

1、Spring AOP(代理方式)

  • 基于 JDK 动态代理(接口) 或 CGLIB 代理(无接口)
  • 只对 Spring 容器中的 Bean 有效
  • 实现方式:
    • 通过 @Aspect + @Before/@After/@Around 注解
    • 通知(Advice)、切点(Pointcut)、织入(Weaving)
  • 优点: 简单,Spring 原生支持
  • 缺点: 不能拦截 private / protected / 构造器 等,方法内部调用也无效

2、AspectJ AOP(编译期织入)

  • 使用 AspectJ 编译器(ajc)或者 LTW(Load-Time Weaving)
  • 可以在编译阶段就将切面逻辑织入字节码
  • 能力强大,支持更多语法

如何启用?

  • 配置 AspectJ Maven 插件
  • 使用 Spring 提供的 @EnableLoadTimeWeaving
  • 或者通过 javaagent
  • 优点: 强大、灵活、高性能
  • 缺点: 学习曲线略高,调试不便,不适合频繁变更的业务场景

3、自定义字节码增强(ASM、Javassist、ByteBuddy)

这个就比较硬核了,属于底层玩法,比如:

  • Dubbo、MyBatis 会使用 ASM 或 Javassist 做动态代理增强
  • SpringBoot 3 中 AOT 编译用的是 ByteBuddy

当然一般业务项目我们不会手写这些,但了解原理能帮助你理解背后的魔法。

面试突围实录:我怎么回答的?

面试官问完“Spring AOP 和 AspectJ 有什么区别”之后追问:

“Spring AOP 为什么无法切方法内部调用?有没有办法绕开?”

我当时举了个我们实际项目中用到的“接口拦截器”场景:

“我们有个 @Permission 校验注解,最开始放在方法上,发现方法内调用无效,于是后来把逻辑拆到单独 Bean,并注入调用,确保走代理。”

然后我补充说:

“我们后来调研过 AspectJ,但因为部署环境对编译器有要求,加上项目对热部署有要求,最终还是选择 Spring AOP + 拆分 Bean 的方式。”

面试官听完点了点头:“挺实战的,你们项目里看起来对 AOP 应用挺深入的。”

我心里暗爽,这波危机化解成功!

常见 Spring AOP 面试高频题(附答案)

1、@Aspect 是怎么被 Spring 扫描并启用的?

  • @EnableAspectJAutoProxy 注解启用 AOP 功能
  • 内部通过 AnnotationAwareAspectJAutoProxyCreator 实现
  • 最终是 Spring 的 BeanPostProcessor 机制生成代理

2、@Around/@Before/@After 的执行顺序是?

顺序是:

  • @Around (进入方法前)
  • @Before
  • 目标方法执行
  • @AfterReturning / @AfterThrowing
  • @After
  • @Around (方法返回后)

3、SpringBoot 项目为什么默认可以使用 AOP?

  • SpringBoot 自动导入了 spring-boot-starter-aop,自动开启 AOP 支持

最后:我对 AOP 的理解与建议

AOP 是一把双刃剑。用得好,它可以让你的业务代码干净清晰、关注分离;用不好,容易造成:

  • 隐性逻辑,排查困难
  • 性能问题(代理链太长)
  • 忽略内部调用问题

所以建议你:

  • 用在日志、异常监控、权限校验、限流等“横切关注点”
  • 避免切入复杂业务流程、尤其是会频繁变更的逻辑
  • 项目初期尽量统一 AOP 策略,约定命名、位置、作用范围

END

希望今天的分享能帮你把 Spring AOP 和 AspectJ AOP 理清楚,在关键面试时不再慌张!

别忘了转发 + 收藏,关键时候救命!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!