摘要:大家好,我是小米,今年31岁,一个三年写 Bug 七年修 Bug的 Java 工程师,从10年前的 HelloWorld 写起,现在已经在各种微服务、SpringBoot、MyBatis、Redis、Kafka 的泥潭中游刃有余(或者说挣扎求生 )。
大家好,我是小米,今年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 后可以变成:
切面代码专门记录执行时间、打印日志等。这就是 AOP 的价值!
重点来啦!面试最常考的对比题。我们来从多个维度分析:
举个栗子:Spring AOP 为什么不支持方法内部调用?
这其实是面试官常常“追问”的点。比如你写了一个类:
你以为两个方法都能被 @LogTime 拦截吗?其实并不会。
因为内部方法调用不会走 Spring AOP 的代理链!
Spring AOP 是通过代理对象来拦截方法调用的,只有外部调用代理对象的方法时才会被拦截。
而 createOrder 调用 updateStock 是 this.updateStock,没走代理。怎么解决?
把两个方法拆到不同 Bean 中或者用 AspectJ 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 应用挺深入的。”
1、@Aspect 是怎么被 Spring 扫描并启用的?
@EnableAspectJAutoProxy 注解启用 AOP 功能内部通过 AnnotationAwareAspectJAutoProxyCreator 实现最终是 Spring 的 BeanPostProcessor 机制生成代理2、@Around/@Before/@After 的执行顺序是?
顺序是:
@Around (进入方法前)@Before目标方法执行@AfterReturning / @AfterThrowing@After@Around (方法返回后)3、SpringBoot 项目为什么默认可以使用 AOP?
AOP 是一把双刃剑。用得好,它可以让你的业务代码干净清晰、关注分离;用不好,容易造成:
隐性逻辑,排查困难性能问题(代理链太长)忽略内部调用问题所以建议你:
来源:慧德教教育