动态代理,JDK 动态代理和 CGlib 动态代理的区别?

程序员面试资料大全|各种技术书籍等资料-1000G


一、本质区别

维度 JDK 动态代理 CGLib 动态代理
实现机制 基于接口反射 基于字节码增强(ASM 操作字节码)
代理目标 必须实现至少一个接口 可代理普通类(无需接口)
生成物 $Proxy0 接口代理类 TargetClass$$EnhancerByCGLIB 子类
方法拦截原理 InvocationHandler.invoke() MethodInterceptor.intercept()

二、性能对比(Java 8+ 环境)

操作 JDK 动态代理 CGLib
创建速度 快(缓存复用代理类) 慢(首次需生成字节码)
执行速度 快(直接反射调用) 快(方法索引优化)
内存占用 高(生成额外子类)

实测数据参考(循环调用 1000 万次):

  • JDK:平均 1.2 秒
  • CGLib:平均 1.5 秒(Java 17 下差距缩小至 5% 内)

三、底层原理图解

JDK 动态代理流程
客户端 $Proxy0 InvocationHandler 目标实现类 调用接口方法 转发调用 invoke() 反射调用真实方法 返回结果 返回结果 返回结果 客户端 $Proxy0 InvocationHandler 目标实现类
CGLib 动态代理流程
客户端 代理子类 MethodInterceptor 目标类 调用目标方法 触发 intercept() 调用 FastClass 优化方法 返回结果 返回结果 返回结果 客户端 代理子类 MethodInterceptor 目标类

关键创新:CGLib 的 FastClass 机制通过方法索引直接调用,避免反射开销


四、实战限制与坑点

JDK 代理的致命缺陷
public class UserServiceImpl { // 未实现接口!
    public void save() { ... }
}

// 尝试代理 → 直接报错!
Proxy.newProxyInstance(...); // IllegalArgumentException: UserServiceImpl is not an interface
CGLib 的三大隐患
  1. Final 方法无法代理

    public class OrderService {
        public final void pay() { ... } // 代理类无法重写
    }
    
  2. 构造函数被调用两次

    // 代理时触发:
    // 1. 目标类构造器 
    // 2. 代理子类构造器
    
  3. 默认生成器 OOM 风险

    Enhancer enhancer = new Enhancer();
    enhancer.setUseCache(false); // 禁用缓存 → 重复生成类 → PermGen OOM
    

五、Spring 的智能选择策略

// Spring AOP 代理选择逻辑 (AopProxyUtils)
public static AopProxy createAopProxy(AdvisedSupport config) {
    if (config.isOptimize() || config.isProxyTargetClass() || 
        hasNoUserSuppliedProxyInterfaces(config)) {
        // 满足任一条件则用 CGLib:
        // 1. 强制使用类代理(@EnableAspectJAutoProxy(proxyTargetClass=true))
        // 2. 目标未实现接口
        return new CglibAopProxy(config);
    } else {
        return new JdkDynamicAopProxy(config); // 否则用 JDK 代理
    }
}

六、场景选型指南

场景 推荐方案 原因
代理 Spring Bean(有接口) JDK 动态代理 无侵入、符合 Spring 默认行为
代理普通工具类(无接口) CGLib 唯一选择
高并发执行场景 JDK 动态代理 现代 JDK 反射性能接近直接调用
需代理 final 方法 字节码增强框架 如 ByteBuddy(CGLib 不支持)
Android 开发 JDK 动态代理 CGLib 的 ASM 可能不兼容

七、代码示例对比

JDK 动态代理实现
public class JdkProxyDemo {
    interface Service { void execute(); }
    
    static class RealService implements Service {
        public void execute() { System.out.println("真实业务"); }
    }

    public static void main(String[] args) {
        Service proxy = (Service) Proxy.newProxyInstance(
            Service.class.getClassLoader(),
            new Class[]{Service.class},
            (proxy1, method, args1) -> {
                System.out.println("前置处理");
                Object result = method.invoke(new RealService(), args1);
                System.out.println("后置处理");
                return result;
            });
        proxy.execute();
    }
}
CGLib 动态代理实现
public class CglibProxyDemo {
    static class UserService {
        public void save() { System.out.println("保存用户"); }
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
            System.out.println("事务开始");
            Object result = proxy.invokeSuper(obj, args1);
            System.out.println("事务提交");
            return result;
        });
        
        UserService proxy = (UserService) enhancer.create();
        proxy.save();
    }
}

程序员面试资料大全|各种技术书籍等资料-1000G

你可能感兴趣的:(java,开发语言,动态代理,JDK动态代理,CGlib动态代理)