一.jdk动态代理的原理
java
package com.atguigu.springaop;
import com.atguigu.springaop.service.UserService;
import com.atguigu.springaop.service.impl.UserServiceImpl;
import java.lang.reflect.Proxy;
/**
* JDK动态代理
* 必须基于接口一种代理方式。
* 目标类 和 代理类 都是接口的实现类。
*
* 由于JDK动态代理通过反射创建在内存中创建代理类。扩展代码不能写在代理类中。
*
* 扩展代码要写在 InvocationHandler接口的invoke实现方法中。java.lang.reflect.InvocationHandler
*/
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); // Boolean flag = userService.login(argsArray[0],argsArray[1]);
System.out.println("方法结束执行了..."); //非业务代码 每个方法开始都要执行这行代码,重复代码开发。不利于维护。
return flag;
});
boolean falg = proxyObject.login("admin", "123456");
System.out.println("falg = " + falg);
}
/**
* 在 JDK 动态代理中,我们先创建一个包含真实业务逻辑的目标对象(如 UserServiceImpl),再通过 Proxy.newProxyInstance()
* 动态生成一个实现了相同接口的代理对象。这个代理对象本身不包含业务逻辑,它的核心是通过 InvocationHandler 拦截方法调用,
* 在调用前后插入增强逻辑(如日志、事务),并通过 method.invoke() 反射调用目标对象的真实方法。代理对象是多例的,每次调用 newProxyInstance
* 都会生成一个新实例,其生命周期由代码作用域决定,而非随单次方法调用结束而销毁。
* */
}
二.Cglib动态代理的原理
- DK 代理的
Method.invoke()是纯反射调用,每次都要动态解析方法签名、检查权限,有固定开销; - CGLIB 的
MethodProxy.invokeSuper()基于 FastClass 生成索引化调用逻辑,绕过反射的动态解析,高频调用场景下性能比 JDK 代理高 20%-30%(Spring 5+ 对 JDK 代理做了优化,差距缩小,但 CGLIB 仍略优)。
java
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 的基础元信息
java
运行
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(性能优化的核心)
java
运行
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 实例
java
运行
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 为目标类 / 代理类生成的 "快速调用类",它的核心逻辑是:
java
运行
// 简化版 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.2code:
java
package com.atguigu.springaop;
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 代理 |