一、JDK 动态代理原理
package com.example.proxy;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
/**
* JDK 动态代理示例
* 必须基于接口一种代理方式。
* 目标类和代理类都是接口的实现类。
*/
public class JdkDynamicProxyDemo {
public static void main(String[] args) {
// 目标对象
UserService userService = new UserServiceImpl();
// 创建代理对象。利用反射机制创建代理类对象。
UserService proxyObject = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
(proxyObj, method, argsArray) -> {
System.out.println("方法开始执行了...");
// 非业务代码,每个方法开始都要执行这行代码,重复代码开发,不利于维护。
// 反射调用目标方法
Boolean flag = (Boolean) method.invoke(userService, argsArray);
System.out.println("方法结束执行了...");
return flag;
}
);
boolean flag = proxyObject.login("admin", "123456");
System.out.println("flag = " + flag);
}
}
在 JDK 动态代理中,我们先创建一个包含真实业务逻辑的目标对象(如 UserServiceImpl),再通过 Proxy.newProxyInstance() 动态生成一个实现了相同接口的代理对象。这个代理对象本身不包含业务逻辑,它的核心是通过 InvocationHandler 拦截方法调用,在调用前后插入增强逻辑(如日志、事务),并通过 method.invoke() 反射调用目标对象的真实方法。代理对象是多例的,每次调用 newProxyInstance 都会生成一个新实例,其生命周期由代码作用域决定,而非随单次方法调用结束而销毁。
二、CGLIB 动态代理原理
- JDK 代理的
Method.invoke()是纯反射调用,每次都要动态解析方法签名、检查权限,有固定开销; - CGLIB 的
MethodProxy.invokeSuper()基于 FastClass 生成索引化调用逻辑,绕过反射的动态解析,高频调用场景下性能比 JDK 代理高 20%-30%(Spring 5+ 对 JDK 代理做了优化,差距缩小,但 CGLIB 仍略优)。
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
private static FastClass helper(CreateInfo ci, Class type) {
FastClass.Generator g = new FastClass.Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
MethodProxy 是 CGLIB 为代理方法生成的'高性能调用器',核心解决普通反射 Method.invoke() 性能低的问题。这段代码的核心流程是:创建 MethodProxy → 懒加载初始化 FastClass → 为方法分配索引 → 通过索引快速调用。
2.1 逐方法拆解源码
1. create(...) 方法:初始化 MethodProxy 的基础元信息
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc); // 目标类方法的签名
proxy.sig2 = new Signature(name2, desc); // 代理类方法的签名
proxy.createInfo = new CreateInfo(c1, c2); // 存储目标类/代理类信息
return proxy;
}
参数 / 变量解读:
c1:目标类(如 UserService,真正包含业务逻辑的类);c2:CGLIB 动态生成的代理类(如 UserService$$EnhancerByCGLIB$$xxxx,目标类的子类);desc:方法的字节码描述符(如 login 方法的描述符是(Ljava/lang/String;Ljava/lang/String;)Z,代表'两个 String 参数,返回 boolean');name1:目标类中方法的原始名称(如 login);name2:代理类中对应方法的别名(CGLIB 生成的,如 CGLIB$login$0);sig1/sig2:Signature 是 CGLIB 封装的'方法签名'(方法名 + 描述符),用于唯一标识一个方法;createInfo:临时存储目标类/代理类的核心信息(类加载器、命名规则等),供后续生成 FastClass 使用。
核心作用:为目标方法和代理方法做'身份标记'——相当于给方法办了'身份证',后续生成 FastClass 时,能通过这个'身份证'找到对应的方法。
2. init() 方法:懒加载创建 FastClass(性能优化的核心)
private void init() {
if (this.fastClassInfo == null) { // 双重检查锁:保证线程安全 + 懒加载
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
// 1. 为目标类 (c1) 生成 FastClass
fci.f1 = helper(ci, ci.c1);
// 2. 为代理类 (c2) 生成 FastClass
fci.f2 = helper(ci, ci.c2);
// 3. 为目标方法/代理方法分配'索引'(核心!)
fci.i1 = fci.f1.getIndex(this.sig1); // 目标方法在 FastClass 中的索引
fci.i2 = fci.f2.getIndex(this.sig2); // 代理方法在 FastClass 中的索引
this.fastClassInfo = fci; // 缓存 FastClass 信息,避免重复创建
this.createInfo = null; // 释放临时信息,节省内存
}
}
}
}
关键逻辑解读:
- 懒加载:init() 不会在 create() 时执行,而是第一次调用
methodProxy.invokeSuper()时才触发——如果代理方法从未被调用,就不用生成 FastClass,节省内存和初始化时间; - 双重检查锁(DCL):保证多线程下只会生成一次 FastClass,避免重复创建导致的性能损耗;
- FastClass 索引化:
fci.f1.getIndex(this.sig1)是核心 ——FastClass 会为目标类的所有方法生成一个'索引表'(方法名 → 整数索引),比如 login 方法的索引是 1,logout 是 2。后续调用方法时,只需传索引 1,不用再通过方法名反射找方法,这是性能提升的关键; fastClassInfo:缓存 FastClass 和方法索引,后续调用直接复用,无需重复生成。
3. helper(...) 方法:生成 FastClass 实例
private static FastClass helper(CreateInfo ci, Class type) {
FastClass.Generator g = new FastClass.Generator();
g.setType(type); // 指定为哪个类生成 FastClass(目标类/代理类)
g.setClassLoader(ci.c2.getClassLoader()); // 复用代理类的类加载器
g.setNamingPolicy(ci.namingPolicy); // 遵循 CGLIB 的命名规则(如 FastClass 类名格式:XXX$$FastClassByCGLIB$$xxxx)
g.setStrategy(ci.strategy); // 字节码生成策略(控制 FastClass 的生成规则)
g.setAttemptLoad(ci.attemptLoad); // 尝试从已加载的类中获取 FastClass,避免重复生成
return g.create(); // 核心:动态生成 FastClass 的字节码并加载到 JVM
}
FastClass 的本质:FastClass 是 CGLIB 为目标类/代理类生成的'快速调用类',它的核心逻辑是:
// 简化版 FastClass 生成的代码
public class UserService$$FastClassByCGLIB$$xxxx {
// 方法索引表:索引 → 方法调用
public Object invoke(int index, Object obj, Object[] args) {
UserService target = (UserService) obj;
switch(index) {
case 1: return target.login((String)args[0], (String)args[1]); // 直接调用,无反射
case 2: return target.logout(); // ... 其他方法
}
}
}
可以看到:FastClass 把'通过方法名反射调用'变成了'通过整数索引直接调用',完全是普通的 Java 方法调用,没有任何反射开销。
结合调用流程,看懂性能优化的本质
当你调用 methodProxy.invokeSuper(proxyObj, args) 时,底层执行流程是:
methodProxy.invokeSuper(...) → 触发 init()(首次调用) → helper() 生成目标类/代理类的 FastClass → 为目标方法分配索引(如 login 方法索引=1) → 调用 FastClass.invoke(1, proxyObj, args) → 通过 switch(index) 直接调用目标方法(无反射)
2.2 代码示例
package com.example.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
// ========== 1. 目标类(无需实现接口,仅做核心业务) ==========
class UserService {
public boolean login(String username, String password) {
System.out.println("UserService - login 目标方法执行了");
// 核心业务:登录校验
return "admin".equals(username) && "123456".equals(password);
}
}
// ========== 2. CGLIB 动态代理核心:通用增强逻辑 + 生成代理 ==========
public class CglibDynamicProxyDemo {
public static void main(String[] args) {
// 步骤 1:创建目标对象
UserService target = new UserService();
// 步骤 2:生成动态代理对象(运行时自动创建代理类,继承目标类)
UserService proxy = (UserService) Enhancer.create(
UserService.class, // 目标类的 Class 对象(核心:继承目标类)
(MethodInterceptor) (proxyObj, method, argsArr, methodProxy) -> {
// 增强逻辑 1:方法执行前(如打印日志)
System.out.println("Before " + method.getName() + ": " + argsArr[0]);
// 调用目标类的核心方法
Object result = methodProxy.invoke(target, argsArr);
// 通过方法代理对象调用目标方法
// 增强逻辑 2:方法执行后(如打印结果)
System.out.println("After " + method.getName() + ": " + result);
return result;
}
);
// 步骤 3:调用代理方法(自动触发增强 + 核心业务)
boolean flag = proxy.login("admin", "123456");
System.out.println("flag = " + flag);
}
}
内部 Enhancer e = new Enhancer(); e.setSuperclass(type); e.setCallback(callback); e.create();
CGLIB 动态代理通过 Enhancer 核心工具类实现,无需目标类实现接口,而是以目标类(如 UserService)作为父类动态生成子类作为代理类;Enhancer.create 方法指定目标类 Class 对象和 MethodInterceptor 回调接口,代理类的所有方法调用都会被该回调拦截,回调方法接收代理实例、被拦截方法的反射对象、方法实际参数值和 CGLIB 优化的 methodProxy 对象;调用代理对象方法时,会先执行前置增强逻辑,再通过 methodProxy.invoke(性能优于普通反射)调用目标对象的核心业务方法,执行完成后执行后置增强逻辑并返回结果,这一机制弥补了 JDK 动态代理需实现接口的局限性,同时通过方法代理优化提升了调用性能。
三、对比
| 对比维度 | JDK 动态代理(JDK Proxy) | CGLIB 动态代理(Code Generation Library) |
|---|---|---|
| 底层原理 | 基于接口实现:动态生成一个实现目标接口的代理类(如 $Proxy0),代理类的方法转发给 InvocationHandler | 基于继承实现:动态生成目标类的子类作为代理类,重写目标方法并转发给 MethodInterceptor |
| 核心限制 | 必须要求目标类实现至少一个接口(否则无法生成代理) | 目标类不能是 final(无法继承),目标方法不能是 final(无法重写) |
| 核心类 / 接口 | Proxy.newProxyInstance()、InvocationHandler | Enhancer.create()、MethodInterceptor、MethodProxy |
| 方法调用方式 | Method.invoke()(纯反射,性能中等) | MethodProxy.invokeSuper()(FastClass 索引调用,性能更高) |
| 侵入性 | 目标类必须实现接口(侵入式设计) | 目标类无需实现任何接口(无侵入式设计) |
| 实例化方式 | 代理类实现接口,可直接强转为接口类型 | 代理类继承目标类,可直接强转为目标类类型 |
| Spring AOP 默认选择 | 目标类有接口 → 优先用 JDK 代理 | 目标类无接口 → 自动切换为 CGLIB 代理 |
