在Spring框架中,AOP(面向切面编程)是核心特性之一,也是面试中的高频考点------从代理实现、对象关系到底层调用链路,每一个细节都可能被追问。本文将从基础区别到底层源码,结合实操例子,全面拆解AOP原理,覆盖所有核心疑问,适合需要深入理解AOP、应对面试的开发者,也可作为日常复习的完整笔记。
本文核心覆盖知识点:
-
JDK动态代理 vs CGLIB动态代理(区别+底层实现)
-
JDK与CGLIB的使用场景
-
目标对象、代理对象、真实对象的关系
-
切点、通知、增强器的核心概念
-
Spring AOP代理创建完整流程
-
this.方法()调用AOP不生效的底层原因
-
AOP与循环依赖的交互(早期代理机制)
-
JDK/CGLIB代理类内部结构、方法调用链路(参数+返回值传递)
全程结合源码片段、可运行实例,拒绝抽象,让每一个底层逻辑都能落地理解。
一、核心基础:JDK动态代理 vs CGLIB动态代理(区别+源码+实例)
Spring AOP的底层实现依赖动态代理,主要有两种方式:JDK动态代理和CGLIB动态代理。两者的实现原理、适用场景有本质区别,是面试的第一道高频题。
1.1 核心区别(表格对比+源码佐证)
| 对比项 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现基础 | 基于接口 + Java反射机制,需实现InvocationHandler接口 | 基于继承 + ASM字节码生成框架,需实现MethodInterceptor接口 |
| 目标对象要求 | 必须实现至少一个接口(否则无法生成代理) | 无接口要求,直接继承目标类(不能代理final类/方法) |
| 方法限制 | 只能增强接口中声明的方法,非接口方法无法增强 | 可增强目标类的所有非final方法(final方法无法重写,无法增强) |
| 性能 | 方法调用时需通过反射,性能略逊于CGLIB | 直接生成字节码,调用时无需反射(invokeSuper),性能接近原生方法 |
| Spring默认策略 | 目标对象实现接口时,默认使用JDK动态代理 | 目标对象无接口时,自动使用CGLIB;可通过配置proxyTargetClass=true强制使用 |
1.2 JDK动态代理底层实现(源码+可运行实例)
JDK动态代理的核心是:通过Proxy.newProxyInstance()生成代理类,代理类实现目标接口,并重写接口方法,所有方法调用都会转发到InvocationHandler的invoke()方法中。
步骤1:定义目标接口(必须有)
java
// 目标接口:定义需要增强的方法
public interface UserService {
void addUser(String username);
String getUserById(Integer id);
}
步骤2:实现目标接口(真实对象)
java
// 真实对象:实现接口,包含具体业务逻辑
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("执行真实业务:添加用户 " + username);
}
@Override
public String getUserById(Integer id) {
System.out.println("执行真实业务:查询ID为 " + id + " 的用户");
return "用户" + id;
}
}
步骤3:实现InvocationHandler(代理处理器)
InvocationHandler是JDK代理的核心,负责定义增强逻辑,所有代理方法的调用都会进入invoke()方法。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 代理处理器:实现InvocationHandler,封装增强逻辑
public class JdkProxyHandler implements InvocationHandler {
// 持有真实目标对象(注意:Spring中并非直接持有,后面会讲TargetSource)
private final Object target;
// 构造方法:传入真实目标对象
public JdkProxyHandler(Object target) {
this.target = target;
}
/**
* 所有代理方法调用都会进入此方法
* @param proxy 代理对象本身(JDK生成的$Proxy实例)
* @param method 被调用的目标方法(Method对象)
* @param args 方法调用时传入的参数
* @return 目标方法的返回值
* @throws Throwable 目标方法抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 前置增强(切面逻辑)
System.out.println("JDK代理前置增强:方法 " + method.getName() + " 开始执行");
// 2. 调用真实目标对象的方法(反射调用)
Object result = method.invoke(target, args);
// 3. 后置增强(切面逻辑)
System.out.println("JDK代理后置增强:方法 " + method.getName() + " 执行完毕");
// 4. 返回目标方法的返回值(透传给调用者)
return result;
}
}
步骤4:生成代理对象并测试
通过Proxy.newProxyInstance()生成代理对象,该方法需要3个参数:类加载器、目标接口数组、代理处理器。
java
import java.lang.reflect.Proxy;
public class JdkProxyTest {
public static void main(String[] args) {
// 1. 创建真实目标对象
UserService target = new UserServiceImpl();
// 2. 创建代理处理器(传入真实目标)
JdkProxyHandler handler = new JdkProxyHandler(target);
// 3. 生成代理对象(JDK动态生成$Proxy类实例)
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器(与真实对象一致)
target.getClass().getInterfaces(), // 目标接口数组
handler // 代理处理器
);
// 4. 调用代理对象的方法(会触发invoke()方法)
proxy.addUser("张三");
System.out.println("-------------------");
String user = proxy.getUserById(1001);
System.out.println("代理返回值:" + user);
}
}
运行结果
text
JDK代理前置增强:方法 addUser 开始执行
执行真实业务:添加用户 张三
JDK代理后置增强:方法 addUser 执行完毕
-------------------
JDK代理前置增强:方法 getUserById 开始执行
执行真实业务:查询ID为 1001 的用户
JDK代理后置增强:方法 getUserById 执行完毕
代理返回值:用户1001
JDK代理类内部结构(伪代码,JDK动态生成)
JDK会动态生成一个名为$Proxy0(数字递增)的代理类,该类继承Proxy,实现目标接口,内部逻辑如下(核心是转发方法调用):
java
// JDK动态生成的代理类(伪代码,真实为字节码,无法直接查看)
public final class $Proxy0 extends Proxy implements UserService {
// 缓存目标接口的方法(静态代码块初始化,避免每次调用都获取Method)
private static Method m1; // 对应addUser方法
private static Method m2; // 对应getUserById方法
// 静态代码块:初始化Method对象(反射获取)
static {
try {
m1 = UserService.class.getMethod("addUser", String.class);
m2 = UserService.class.getMethod("getUserById", Integer.class);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
// 构造方法:传入InvocationHandler(与Proxy父类关联)
public $Proxy0(InvocationHandler h) {
super(h);
}
// 重写接口方法:addUser
@Override
public void addUser(String username) {
try {
// 关键:将代理对象、方法、参数传给handler.invoke()
super.h.invoke(this, m1, new Object[]{username});
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
// 重写接口方法:getUserById
@Override
public String getUserById(Integer id) {
try {
// 同样转发到invoke()方法,返回值透传
Object result = super.h.invoke(this, m2, new Object[]{id});
return (String) result;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
1.3 CGLIB动态代理底层实现(源码+可运行实例)
CGLIB(Code Generation Library)的核心是:通过ASM字节码框架,动态生成目标类的子类,重写目标类的非final方法,所有方法调用都会转发到MethodInterceptor的intercept()方法中。
注意:需要导入CGLIB依赖(SpringBoot已内置,无需额外导入)。
步骤1:定义目标类(无需接口)
java
// 目标类:无需实现接口,包含具体业务逻辑
public class OrderService {
public void createOrder(String orderNo) {
System.out.println("执行真实业务:创建订单 " + orderNo);
}
public Integer getOrderCount() {
System.out.println("执行真实业务:查询订单总数");
return 100;
}
}
步骤2:实现MethodInterceptor(方法拦截器)
MethodInterceptor是CGLIB代理的核心,负责定义增强逻辑,所有代理方法的调用都会进入intercept()方法。
java
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// CGLIB方法拦截器:实现MethodInterceptor
public class CglibProxyInterceptor implements MethodInterceptor {
/**
* 所有代理方法调用都会进入此方法
* @param obj 代理对象本身(CGLIB生成的子类实例)
* @param method 被调用的目标方法(父类方法)
* @param args 方法调用时传入的参数
* @param proxy MethodProxy:快速调用父类方法的工具(避免反射)
* @return 目标方法的返回值
* @throws Throwable 目标方法抛出的异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 1. 前置增强(切面逻辑)
System.out.println("CGLIB代理前置增强:方法 " + method.getName() + " 开始执行");
// 2. 调用真实目标方法(通过MethodProxy.invokeSuper,直接调用父类方法,无反射)
Object result = proxy.invokeSuper(obj, args);
// 3. 后置增强(切面逻辑)
System.out.println("CGLIB代理后置增强:方法 " + method.getName() + " 执行完毕");
// 4. 返回目标方法的返回值(透传给调用者)
return result;
}
}
步骤3:生成CGLIB代理对象并测试
通过Enhancer(CGLIB核心工具类)生成代理对象,需要设置父类(目标类)和回调(MethodInterceptor)。
java
import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 1. 创建CGLIB增强器(用于生成代理类)
Enhancer enhancer = new Enhancer();
// 2. 设置父类(目标类):代理类将继承该类
enhancer.setSuperclass(OrderService.class);
// 3. 设置回调(方法拦截器):所有方法调用都会进入拦截器
enhancer.setCallback(new CglibProxyInterceptor());
// 4. 生成代理对象(CGLIB动态生成子类实例)
OrderService proxy = (OrderService) enhancer.create();
// 5. 调用代理对象的方法(会触发intercept()方法)
proxy.createOrder("ORDER_20260421");
System.out.println("-------------------");
Integer count = proxy.getOrderCount();
System.out.println("代理返回值:" + count);
}
}
运行结果
text
CGLIB代理前置增强:方法 createOrder 开始执行
执行真实业务:创建订单 ORDER_20260421
CGLIB代理后置增强:方法 createOrder 执行完毕
-------------------
CGLIB代理前置增强:方法 getOrderCount 开始执行
执行真实业务:查询订单总数
CGLIB代理后置增强:方法 getOrderCount 执行完毕
代理返回值:100
CGLIB代理类内部结构(伪代码,动态生成)
CGLIB会动态生成一个名为OrderService$$EnhancerByCGLIB$$xxx(随机后缀)的子类,继承目标类,重写所有非final方法,内部逻辑如下:
java
// CGLIB动态生成的代理类(伪代码)
public class OrderService$$EnhancerByCGLIB extends OrderService {
// 持有方法拦截器(回调对象)
private MethodInterceptor interceptor;
// 缓存MethodProxy(快速调用父类方法的工具)
private static MethodProxy createOrderMethodProxy;
private static MethodProxy getOrderCountMethodProxy;
// 静态代码块:初始化MethodProxy
static {
// 通过MethodProxy.create()生成快速调用器
createOrderMethodProxy = MethodProxy.create(
OrderService.class, // 父类(目标类)
OrderService$$EnhancerByCGLIB.class, // 子类(代理类)
"(Ljava/lang/String;)V", // 方法参数签名(createOrder)
"createOrder", // 目标方法名
"CGLIB$createOrder$0" // 代理类内部的方法名
);
getOrderCountMethodProxy = MethodProxy.create(
OrderService.class,
OrderService$$EnhancerByCGLIB.class,
"()I",
"getOrderCount",
"CGLIB$getOrderCount$1"
);
}
// 重写父类方法:createOrder
@Override
public final void createOrder(String orderNo) {
try {
// 关键:将代理对象、方法、参数、MethodProxy传给intercept()
interceptor.intercept(
this, // 代理对象本身
createOrderMethodProxy.getOriginalMethod(), // 目标方法
new Object[]{orderNo}, // 方法参数
createOrderMethodProxy // MethodProxy
);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
// 重写父类方法:getOrderCount
@Override
public final Integer getOrderCount() {
try {
Object result = interceptor.intercept(
this,
getOrderCountMethodProxy.getOriginalMethod(),
new Object[0],
getOrderCountMethodProxy
);
return (Integer) result;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
// 代理类内部方法:调用父类(目标类)的真实方法
final void CGLIB$createOrder$0(String orderNo) {
super.createOrder(orderNo);
}
final Integer CGLIB$getOrderCount$1() {
return super.getOrderCount();
}
}
1.4 关键补充:Spring中JDK/CGLIB的复用机制(避免类爆炸)
很多开发者会有疑问:如果有1000个不同接口/类需要生成代理,是不是会创建1000个InvocationHandler/MethodInterceptor类?会不会导致类爆炸?
答案:不会,Spring做了高度复用和缓存,核心逻辑如下:
-
JDK代理:InvocationHandler类只有1个(Spring内部的JdkDynamicAopProxy),1000个代理对象对应1000个JdkDynamicAopProxy实例(每个实例持有自己的目标对象和切面配置),类不重复,实例轻量(仅持有引用,占用内存极小)。
-
CGLIB代理:MethodInterceptor类只有1个(Spring内部的CglibAopProxy.DynamicAdvisedInterceptor),1000个不同目标类对应1000个CGLIB代理子类(每个目标类生成1个子类,缓存复用),每个代理对象对应1个Interceptor实例,同样类不重复。
核心:类复用,实例独立(每个实例持有自己的目标对象),避免重复创建类,性能无压力。
二、核心概念:目标对象、代理对象、真实对象的关系
这是理解AOP的基础,也是面试中常考的细节,很多开发者会混淆三者的关系,一句话总结:代理对象包装真实对象,外部调用只接触代理对象,代理对象负责转发调用+执行切面逻辑。
2.1 三者定义
-
真实对象(目标对象):你自己编写的业务类实例(如UserServiceImpl、OrderService),包含具体的业务逻辑,是AOP增强的目标。
-
代理对象:Spring通过JDK/CGLIB动态生成的对象,要么实现目标接口(JDK),要么继承目标类(CGLIB),内部持有真实对象(JDK)或自身包含真实对象逻辑(CGLIB)。
-
关系总结:
-
JDK代理:代理对象 ≠ 真实对象,代理对象持有真实对象的引用,调用代理方法时,转发到真实对象的方法。
-
CGLIB代理:代理对象是真实对象的子类,代理对象自身包含真实对象的逻辑(通过super调用父类方法),无需额外持有真实对象引用。
-
2.2 关系示意图(极简)
JDK代理关系
text
代理对象($Proxy0) → 持有 → JdkDynamicAopProxy实例 → 持有 → TargetSource → 持有 → 真实对象(UserServiceImpl)
CGLIB代理关系
text
代理对象(OrderService$$EnhancerByCGLIB) → 继承 → 真实对象(OrderService)
代理对象 → 持有 → DynamicAdvisedInterceptor实例(增强逻辑)
三、核心概念:切点、通知、增强器
AOP的核心是"切面",而切面由"切点"和"通知"组成,增强器是Spring内部对切面的统一封装,三者的关系是:增强器 = 切点 + 通知。
3.1 切点(Pointcut)
定义:哪些方法需要被增强,本质是一个"筛选规则",通过execution表达式、注解等方式指定需要拦截的方法。
核心作用:精准定位需要增强的方法,避免无差别增强。
示例(execution表达式,最常用):
java
// 拦截com.example.service包下所有类的所有public方法
@Pointcut("execution(public * com.example.service.*.*(..))")
public void servicePointcut() {}
Spring源码中,切点的核心接口是Pointcut,常用实现有JdkRegexpMethodPointcut、AnnotationMatchingPointcut等。
3.2 通知(Advice)
定义:增强逻辑在什么时候执行、执行什么,是切面的具体实现,分为5种类型,覆盖方法调用的全生命周期:
-
@Before:目标方法执行前执行(前置增强)
-
@AfterReturning:目标方法正常执行完毕后执行(后置增强,可获取返回值)
-
@AfterThrowing:目标方法抛出异常后执行(异常增强)
-
@After:目标方法无论是否异常,最终都会执行(最终增强)
-
@Around:环绕目标方法,可在方法执行前、后、异常时执行增强逻辑(最强大,可控制目标方法是否执行)
示例(环绕通知):
java
@Around("servicePointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前置:方法即将执行");
// 执行目标方法(必须调用,否则目标方法不执行)
Object result = joinPoint.proceed();
System.out.println("环绕后置:方法执行完毕,返回值:" + result);
return result;
}
3.3 增强器(Advisor)
定义:Spring内部对"切点+通知"的统一封装,是AOP代理创建的核心依据。Spring会将所有切面解析为增强器,然后根据增强器生成代理对象。
核心接口:Advisor,常用实现有PointcutAdvisor(包含切点和通知)、IntroductionAdvisor等。
源码层面:Spring在创建代理时,会收集所有适用于当前Bean的Advisor,然后将其封装到AdvisedSupport中,供代理处理器(JdkDynamicAopProxy/CglibAopProxy)使用。
四、Spring AOP 代理创建完整流程(源码级)
Spring AOP代理的创建是一个"筛选-封装-生成"的过程,全程由Spring容器自动完成,核心流程如下(结合源码片段,简化理解):
4.1 核心流程(7步)
-
实例化真实对象:Spring容器扫描@Component、@Service等注解,反射创建真实目标对象(如UserServiceImpl实例)。
-
筛选切面(增强器):Spring收集容器中所有的Advisor,筛选出适用于当前真实对象的增强器(通过切点匹配,判断该对象的方法是否需要增强)。
-
判断是否需要生成代理:如果有适用于当前对象的增强器,就需要生成代理;否则,直接将真实对象放入容器,无需代理。
-
选择代理方式(JDK/CGLIB):
-
如果真实对象实现了接口 → 默认使用JDK动态代理。
-
如果真实对象没有实现接口 → 使用CGLIB动态代理。
-
如果配置了proxyTargetClass=true → 强制使用CGLIB动态代理。
-
-
封装AdvisedSupport:将筛选出的增强器、真实对象(包装为TargetSource)、代理配置等封装到AdvisedSupport中,作为代理创建的核心参数。
-
生成代理对象:
-
JDK代理:通过Proxy.newProxyInstance()生成代理对象,传入类加载器、目标接口、JdkDynamicAopProxy实例(持有AdvisedSupport)。
-
CGLIB代理:通过Enhancer生成代理对象,设置父类(真实对象)、回调(CglibAopProxy实例,持有AdvisedSupport)。
-
-
将代理对象放入容器:Spring容器中存储的是代理对象,而非真实对象;外部通过@Autowired注入的,也是代理对象。
4.2 核心源码片段(简化)
Spring中代理创建的核心类是DefaultAopProxyFactory,其createAopProxy()方法负责选择代理方式并生成代理:
java
public class DefaultAopProxyFactory implements AopProxyFactory {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 1. 配置强制使用CGLIB,或目标对象无接口 → 使用CGLIB
if (config.isProxyTargetClass() || config.getTargetClass().getInterfaces().length == 0) {
return new CglibAopProxy(config);
} else {
// 2. 目标对象有接口 → 使用JDK代理
return new JdkDynamicAopProxy(config);
}
}
}
五、高频面试题:this.方法() 为什么AOP不生效?
这是面试中最常考的AOP问题,很多开发者在项目中也会踩坑,核心原因:this指向真实对象,而非代理对象,跳过了代理的增强逻辑。
5.1 问题复现(代码示例)
java
@Service
public class UserService {
// 无AOP增强的方法
public void a() {
// 调用同类中带AOP增强的方法
this.b(); // this指向UserServiceImpl实例(真实对象)
}
// 带AOP增强的方法(比如事务、日志)
@Transactional
public void b() {
System.out.println("执行方法b,需要事务增强");
}
}
现象:调用userService.a()时,方法b()的事务增强不生效,因为this.b()调用的是真实对象的方法,没有经过代理对象,自然不会触发AOP增强。
5.2 底层原因(结合代理结构)
-
Spring AOP的增强逻辑是在代理对象中实现的,只有通过代理对象调用方法,才会触发invoke()/intercept()方法,执行增强逻辑。
-
this关键字指向的是当前类的实例,也就是真实对象(UserServiceImpl),而非Spring生成的代理对象(
$Proxy0或UserService$$EnhancerByCGLIB)。 -
真实对象中没有任何AOP增强逻辑,所以this.b()直接执行真实方法,跳过增强。
5.3 解决方案(3种,优先推荐前2种)
- 注入自身,通过代理对象调用:
java
@Service
public class UserService {
// 注入自身(注入的是代理对象)
@Autowired
private UserService self;
public void a() {
self.b(); // 调用代理对象的方法,触发AOP增强
}
@Transactional
public void b() {
System.out.println("执行方法b,事务增强生效");
}
}
- 通过AopContext获取当前代理对象:
java
@Service
public class UserService {
public void a() {
// 获取当前代理对象
UserService proxy = (UserService) AopContext.currentProxy();
proxy.b(); // 调用代理对象的方法
}
@Transactional
public void b() {
System.out.println("执行方法b,事务增强生效");
}
}
注意:需要配置expose-proxy=true(SpringBoot中可在application.yml中配置)。
- 将方法拆分到不同类中:将方法a()和方法b()拆分到两个不同的Service中,通过@Autowired注入调用,自然会经过代理对象。
六、AOP与循环依赖的交互(早期代理)
Spring解决循环依赖的核心是"三级缓存",而当循环依赖的Bean需要AOP代理时,会触发"早期代理"机制,否则会导致AOP增强失效。
6.1 先回顾:Spring循环依赖的三级缓存
-
一级缓存(singletonObjects):存储完全初始化完成的单例Bean。
-
二级缓存(earlySingletonObjects):存储提前暴露的、未完全初始化的Bean(仅实例化,未填充属性、未初始化)。
-
三级缓存(singletonFactories):存储Bean的工厂对象,用于提前暴露Bean(生成未完全初始化的Bean)。
6.2 问题场景:循环依赖+AOP
假设存在循环依赖:UserService → OrderService → UserService,且UserService需要AOP代理(如事务)。
如果没有早期代理,流程会如下(错误):
-
UserService实例化(未初始化,未生成代理)。
-
UserService需要注入OrderService,去创建OrderService。
-
OrderService实例化,需要注入UserService,从三级缓存获取UserService的未初始化实例(真实对象)。
-
OrderService初始化完成,注入到UserService中,UserService初始化完成,生成代理对象。
-
问题:OrderService中注入的是UserService的真实对象,而非代理对象,导致UserService的AOP增强失效。
6.3 解决方案:早期代理(提前生成代理)
Spring为了解决上述问题,会在循环依赖场景下,提前为Bean生成代理对象,并将代理对象放入二级缓存,确保注入的是代理对象,而非真实对象。
核心逻辑:
-
当Bean需要AOP代理,且存在循环依赖时,Spring会在Bean实例化后、填充属性前,提前生成代理对象。
-
将提前生成的代理对象放入二级缓存(earlySingletonObjects)。
-
其他Bean注入时,从二级缓存获取的是代理对象,而非真实对象,确保AOP增强生效。
6.4 源码关键点
Spring中负责提前生成代理的核心方法是AbstractAutowireCapableBeanFactory的getEarlyBeanReference():
java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
// 如果Bean需要AOP代理,提前生成代理对象
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
总结:循环依赖会触发AOP早期代理,确保注入的是代理对象,避免AOP增强失效。
七、底层细节:JDK/CGLIB代理类的方法调用链路(参数+返回值传递)
前面我们讲了代理类的结构,这里重点拆解:代理类如何知道调用哪个目标方法、如何调用目标方法、参数和返回值如何传递,彻底打通底层链路。
7.1 JDK代理的调用链路(完整流程)
-
外部调用代理方法:调用者通过代理对象($Proxy0)调用addUser("张三")。
-
代理类方法转发:进入$Proxy0.addUser()方法,该方法内部已绑定固定的Method对象(m1,对应addUser方法),将代理对象(this)、Method对象(m1)、参数("张三")打包,调用handler.invoke()。
-
invoke()方法执行增强逻辑:进入JdkDynamicAopProxy.invoke(),先执行前置增强,然后通过TargetSource获取真实目标对象(UserServiceImpl实例)。
-
调用真实目标方法:通过Method.invoke(真实对象, 参数),反射调用真实对象的addUser("张三")方法,执行业务逻辑。
-
返回值透传:真实方法执行完毕,返回值(无返回值则为null)传递给invoke()方法,执行后置增强,然后将返回值透传给代理类。
-
代理类返回结果:代理类将返回值(无返回值则直接返回)传递给调用者,整个链路结束。
7.2 CGLIB代理的调用链路(完整流程)
-
外部调用代理方法:调用者通过代理对象(OrderService$$EnhancerByCGLIB)调用createOrder("ORDER_20260421")。
-
代理类方法转发:进入代理类的createOrder()方法,该方法内部绑定固定的MethodProxy,将代理对象(this)、目标方法、参数、MethodProxy打包,调用intercept()。
-
intercept()方法执行增强逻辑:进入DynamicAdvisedInterceptor.intercept(),先执行前置增强。
-
调用真实目标方法:通过MethodProxy.invokeSuper(代理对象, 参数),直接调用代理对象的父类(OrderService)的createOrder方法,无需反射,执行业务逻辑。
-
返回值透传:真实方法执行完毕,返回值传递给intercept()方法,执行后置增强,然后将返回值透传给代理类。
-
代理类返回结果:代理类将返回值传递给调用者,整个链路结束。
7.3 关键细节:参数和返回值传递
-
参数传递:
-
JDK代理:外部传入的参数 → 代理类打包成Object[] args → 传给invoke() → 反射调用时传入真实对象。
-
CGLIB代理:外部传入的参数 → 代理类打包成Object[] args → 传给intercept() → invokeSuper()传入代理对象(父类方法使用该参数)。
-
-
返回值传递:
-
真实方法返回值 → invoke()/intercept() → 代理类 → 强转为目标方法的返回值类型 → 传递给调用者。
-
如果真实方法无返回值(void),则返回null,代理类无需强转,直接返回。
-
八、总结
本文覆盖了AOP的核心知识点,从基础到底层,从实例到源码,彻底拆解面试重灾区,最后用几句话总结核心要点,方便背诵:
-
Spring AOP底层依赖动态代理:有接口用JDK(接口+反射),无接口用CGLIB(继承+字节码),类复用、实例独立。
-
代理对象包装真实对象,外部调用只接触代理,增强逻辑在代理处理器(invoke/intercept)中执行。
-
切点定义"增强哪些方法",通知定义"怎么增强、什么时候增强",增强器=切点+通知。
-
this.方法()不生效:this指向真实对象,跳过代理,需通过代理对象调用。
-
循环依赖+AOP:触发早期代理,提前生成代理对象放入二级缓存,确保注入的是代理,增强生效。
-
方法调用链路:外部调用→代理类转发→增强逻辑→真实方法→返回值透传。
掌握以上内容,无论面试中被问到AOP的哪个细节,都能从容应对;同时也能轻松解决项目中AOP相关的踩坑问题。