【Spring底层分析】AOP的cligb代理和jdk代理
AOP的cligb代理和jdk代理
spring默认创建的是clibg代理对象,如果想要让spring创建jdk代理对象,那么需要:
1、确保目标类实现至少一个接口
2、配置Spring强制使用JDK代理,@EnableAspectJAutoProxy(proxyTargetClass = false)
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false) // 关键设置
public class AppConfig {// 配置类内容
}
cligb代理对象代理流程:
- Spring为某些Bean创建CGLIB代理对象
- 调用方法时实际调用代理对象的方法
- 代理对象执行
intercept()
方法 - 生成拦截器链(由多个
MethodInterceptor
组成) - 链执行器按顺序执行拦截器链中的每个拦截器的invoke方法,实际就是调用增强方法。
- 在合适的时机调用原始目标方法
无论是创建CGLIB代理对象、还是执行intercept()
方法,实际上都是CglibAopProxy类里的方法。
class CglibAopProxy implements AopProxy, Serializable{public Object getProxy() {}public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//这里可以具体看我的另一篇文章,有详细介绍源码:https://blog.csdn.net/m0_73866527/article/details/148384920?spm=1001.2014.3001.5501}
}
jdk代理对象代理流程:
- Spring为某些Bean创建jdk代理对象
- 调用方法时实际调用代理对象的方法
- 代理对象执行
invoke()
方法 - 生成拦截器链(由多个
MethodInterceptor
组成) - 链执行器按顺序执行拦截器链中的每个拦截器的invoke方法,实际就是调用增强方法。
- 在合适的时机调用原始目标方法
无论是创建CGLIB代理对象、还是执行intercept()
方法,实际上都是JdkDynamicAopProxy类里的方法。
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler {public Object getProxy() {return getProxy(ClassUtils.getDefaultClassLoader());}public Object getProxy(@Nullable ClassLoader classLoader) {// 1. 获取目标接口Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);// 2. 关键:创建JDK代理实例return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);}// 实现InvocationHandler接口public Object invoke(Object proxy, Method method, Object[] args) {// 拦截逻辑// 获取目标对象(原始Bean)Target target = getTarget();// 创建拦截器链 - 与你熟悉的CGLIB完全一致!List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 创建方法调用链执行器MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);// 执行拦截器链 - 核心流程与你已知的CGLIB完全一致return invocation.proceed();}
}
这时候你肯定有疑惑,为什么代理对象执行目标方法时,会自动执行intercept方法或invoke方法?
在对象创建时通过字节码生成技术,将调用路径"刻印"在代理对象的DNA中,使得后续所有方法调用都能自动路由到正确的处理逻辑!
(这里留个坑,先不细挖了)
那么你又会有疑惑,为什么这两个代理方式差不多,为什么还需要有这两种代理方式呢?
维度 | JDK动态代理 | CGLIB代理 | 差异影响 |
---|---|---|---|
代理机制 | 接口代理(实现相同接口) | 子类代理(继承目标类) | 根本设计哲学不同 |
目标要求 | 必须实现接口 | 类不能是final | 适用场景分化 |
性能特点 | 创建快(≈15ns),调用慢(反射开销) | 创建慢(≈200ns),调用快(直接调用) | 高频调用场景选择关键 |
方法覆盖 | 只能代理接口方法 | 可代理所有非final方法 | 功能覆盖范围不同 |
内存占用 | 轻量级(约256字节) | 重量级(约1-2KB) | 资源敏感系统选择依据 |
- JDK代理是通过反射调用目标方法(因为代理对象和目标对象没有继承关系,只有共同的接口,所以只能反射调用目标对象的方法)。
- CGLIB代理是通过直接调用(通过方法索引,类似直接调用)父类(目标类)的方法(因为代理对象是目标对象的子类,所以可以调用父类方法)。
- JDK代理 = "翻译官"模式
- 需要懂双方语言(接口)
- 每次沟通都要翻译(反射)
- 适合临时简单任务
- CGLIB代理 = "机械臂"模式
- 直接嵌入操作环境(继承)
- 预设精确操作指令(直接调用)
- 适合高频重复任务