AOP实现是Spring面试IOC之后的经典追问。考察你对代理模式和字节码层差异的理解——直接关系到生产里"同类自调用@Transactional失效"这个常见坑。
核心:AOP生成一个包裹真实bean的代理对象,方法调用先跑advice(before/after/around)再委托给真实方法。
JDK动态代理:只能代理接口。Proxy.newProxyInstance运行时生成实现目标接口的类,方法走InvocationHandler.invoke。
CGLIB:运行时生成目标类的子类字节码。能代理普通类(不实现接口也行)。但final类和final方法代理不了。
Spring规则:目标实现至少一个接口就用JDK代理,否则用CGLIB。Spring Boot 2.x+默认全用CGLIB(proxyTargetClass=true)。
关键坑:AOP只对走代理的调用生效。同类内this.methodB()直接调原对象,不经过代理,advice不触发——@Transactional自调用失效就是这个原因。
Spring AOP生成一个代理对象包裹你的bean,外部调用方持有代理,每次方法调用先跑advice链(before/after/around)再委托真实方法。两种代理机制。JDK动态代理用java.lang.reflect.Proxy运行时生成实现目标接口的类,方法调用走InvocationHandler的invoke()——里面判断方法名、应用advice。只能在目标实现了接口时用。CGLIB用字节码操作(底层ASM)运行时生成目标类的子类。方法被子类override,调用进子类先应用advice再super调父类。能代理普通类,但final类不能继承、final/private方法不能override。Spring默认规则是"有接口用JDK代理,没接口用CGLIB"。Spring Boot 2.x翻转了默认值——全用CGLIB(proxyTargetClass=true),避免调用方强转成具体类时的意外。生产最关键的坑是自调用失效。Foo类有方法a()和b(),b()加了@Transactional。a()里写this.b(),调的是原始Foo对象的b()方法,没经过代理,事务advice根本不跑。解决:把bean注入自己;或者用AopContext.currentProxy()拿到代理;或者重构把@Transactional方法挪到另一个bean里调。
AOP一定要带出"自调用失效"这个生产头号坑。
知道Spring Boot 2.x+默认CGLIB,"有接口就JDK"是老知识。
提到final类/final方法限制是字节码层理解的信号。
忘了proxyTargetClass这个配置,即答侠可以实时提示。
CGLIB靠继承,final类不能被继承,CGLIB会抛异常。final方法同理不能override。
CGLIB调用更快(直接override),生成慢。JDK是反射。稳态运行差异忽略不计。
bean.getClass().getName()——CGLIB带$$EnhancerBySpringCGLIB后缀,JDK代理是$Proxy数字。