Jdk动态代理 Cglib动态代理
Jdk动态代理 & Cglib动态代理
一、Jdk动态代理
当调用Proxy.newProxyInstance()时,JDK会在内存中动态生成一个代理类,这个类大致如下:
public interface UserService{public String getUserById(int id);public void createUser(String name);
}
public class UserServiceImpl implements UserService{public String getUserById(int id){// 执行实际方法。。。}public void createUser(String name){// 执行实际方法。。。}
}
// JDK动态生成的代理类(伪代码)
public final class $Proxy0 extends Proxy implements UserService {private static Method m1; // getUserById方法private static Method m2; // createUser方法// 静态初始化块:通过反射获取Method对象static {try {m1 = Class.forName("UserService").getMethod("getUserById", int.class);m2 = Class.forName("UserService").getMethod("createUser", String.class);} catch (Exception e) {throw new Error(e);}}// 构造函数public $Proxy0(InvocationHandler h) {super(h);}// 重写接口方法 - 将调用转发给InvocationHandlerpublic String getUserById(int id) {try {// 这里传递预先获取的Method对象!return (String) super.h.invoke(this, m1, new Object[]{id});} catch (Throwable e) {throw new RuntimeException(e);}}// 重写接口方法public void createUser(String name) {try {// 这里传递预先获取的Method对象!super.h.invoke(this, m2, new Object[]{name});} catch (Throwable e) {throw new RuntimeException(e);}}
}
// 测试
public class MethodSourceExample {public static void main(String[] args) {// 创建出实际对象UserService realService = new UserServiceImpl();// 创建出代理对象UserService proxy = (UserService) Proxy.newProxyInstance(MethodSourceExample.class.getClassLoader(),// 类加载器new Class[]{UserService.class},// 类信息,作用是在创建代理对象时,可以通过这个信息调用反射API从而获取方法信息Methodnew InvocationHandler() {// 处理器,作用是当调用代理对象的方法实际上调用的是这个处理器的invoke方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 这个method参数就是JDK传递过来的!System.out.println("调用的方法: " + method.getName());System.out.println("方法参数: " + Arrays.toString(args));System.out.println("方法返回类型: " + method.getReturnType());return method.invoke(realService, args);}});// 调用方法 - 会自动传递对应的Method对象proxy.createUser("张三");String result = proxy.getUserById(123);}
}
总结:jdk代理对象实现了与实际对象一致的接口,重写了这个接口的所有方法。当调用代理对象的代理方法时,执行的就是处理器的invoke方法。这个invoke方法里可以通过反射调用到实际方法。
JDK代理基于接口、JDK代理使用反射调用、JDK代理可以代理接口中的默认方法。
二、Cglib动态代理
Cglib动态代理:它在运行时动态生成被代理类的子类,从而实现对类的代理。
// 目标类 - 不需要实现任何接口
class UserService {public void createUser(String name) {System.out.println("创建用户: " + name);}public void deleteUser(String name) {System.out.println("删除用户: " + name);}// 一个组合方法,内部调用其他方法public void manageUser(String name) {createUser(name);deleteUser(name);}// final方法,CGLIB无法代理public final void finalMethod() {System.out.println("这是一个final方法");}
}
// 方法拦截器 - 类似于JDK代理中的InvocationHandler
class LoggingInterceptor implements MethodInterceptor {// 前置通知方法private void beforeMethod() {System.out.println("[日志] 方法开始执行");}// 后置通知方法private void afterMethod() {System.out.println("[日志] 方法执行完成");}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {beforeMethod(); // 方法执行前的增强逻辑// 调用原始方法(通过MethodProxy)Object result = proxy.invokeSuper(obj, args);afterMethod(); // 方法执行后的增强逻辑return result;}
}
// 测试
public class CglibExample {public static void main(String[] args) {// 1. 创建Enhancer对象 - 用于生成代理类Enhancer enhancer = new Enhancer();// 2. 设置父类(被代理的类)enhancer.setSuperclass(UserService.class);// 3. 设置回调(方法拦截器)enhancer.setCallback(new LoggingInterceptor());// 4. 创建代理对象UserService userServiceProxy = (UserService) enhancer.create();System.out.println("=== 调用单个方法 ===");userServiceProxy.createUser("张三");System.out.println("\n=== 调用组合方法 ===");userServiceProxy.manageUser("李四");System.out.println("\n=== 调用final方法 ===");userServiceProxy.finalMethod();}
}
// 伪代码:CGLIB生成的代理类大致结构
public class UserService$$EnhancerByCGLIB extends UserService {private MethodInterceptor interceptor;private static final MethodProxy methodProxy1;private static final MethodProxy methodProxy2;static {// 静态初始化:创建MethodProxy对象}// 重写父类方法public void createUser(String name) {// 调用拦截器interceptor.intercept(this, Method对象表示createUser, new Object[]{name}, MethodProxy对象);}// 其他方法类似...
}
三、Cglib动态代理特点
MethodProxy的作用:
public class MethodProxy {public Object invokeSuper(Object obj, Object[] args) {// 使用FastClass快速调用父类方法return fastClassInfoProxy.invoke(methodIndex, obj, args);}
}
FastClass的工作原理:
FastClass为每个方法分配唯一索引,通过索引直接调用方法:
// FastClass的近似实现
public class FastClass {public Object invoke(int index, Object obj, Object[] args) {switch (index) {case 0: return ((TargetClass)obj).method1((String)args[0]);case 1: return ((TargetClass)obj).method2((Integer)args[0]);// ...}}
}
CGLIB选择字节码分析是因为它能代理普通类,知道被代理的类里的各个方法信息。而JDK动态代理只能代理接口,而接口的方法信息通过反射API就能完整获取,因此不需要复杂的字节码分析。因此CGLIB代理能通过方法索引直接调用,避免反射开销。
四、CGLIB的优势与局限
1. 优势
- 高性能:FastClass机制使方法调用接近直接调用速度
- 灵活性:可以代理普通类,不仅仅是接口
- 功能强大:支持复杂的方法拦截和增强场景
2. 局限性
- 不能代理final类和方法:因为基于继承机制
- 创建开销较大:字节码分析和生成比JDK代理更耗时
- 依赖ASM库:需要额外的第三方依赖
五、实际应用场景
1. Spring框架中的应用
// Spring配置强制使用CGLIB代理
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {// ...
}
2. 适用场景
- 代理没有实现接口的类
- 需要高性能方法调用的场景
- 需要代理私有方法的特殊场景