重学Java基础篇—对比动态代理JDK与CGLIB

胶片质感

一、核心原理对比

1. JDK动态代理

实现机制

  • 基于接口的代理
  • 使用java.lang.reflect.Proxy生成代理类
  • 依赖InvocationHandler接口实现方法拦截

核心代码示例

// 目标接口
public interface UserService {
    void save();
}

// 代理处理器
public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method");
        Object result = method.invoke(target, args);
        System.out.println("After method");
        return result;
    }
}

// 使用示例
UserService proxy = (UserService) Proxy.newProxyInstance(
    ClassLoader.getSystemClassLoader(),
    new Class[]{UserService.class},
    new MyInvocationHandler(target)
);

2. CGLIB动态代理

实现机制

  • 基于继承的代理
  • 使用ASM字节码操作框架生成子类
  • 依赖MethodInterceptor接口实现方法拦截

核心代码示例

// 目标类(无需接口)
public class UserServiceImpl {
    public void save() {
        System.out.println("Saving user");
    }
}

// 代理生成器
public class CglibProxy {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("Before method");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("After method");
                return result;
            }
        });
        return enhancer.create();
    }
}

// 使用示例
UserServiceImpl proxy = (UserServiceImpl) CglibProxy.getProxy(UserServiceImpl.class);

二、关键技术对比

对比维度JDK动态代理CGLIB代理
实现基础接口实现类继承
生成方式运行时生成接口实现类通过修改字节码生成目标类的子类
执行速度较慢(反射调用)较快(直接调用)
生成速度较快(首次生成)较慢(需要生成字节码)
方法限制只能代理接口方法可代理除final外的所有方法
外部依赖无需额外库需要引入cglib库
内存消耗较小较大(生成多个代理类)
兼容性支持所有Java版本需要处理字节码版本兼容问题

三、核心实现流程

1. JDK动态代理生成流程

定义接口
实现InvocationHandler
调用Proxy.newProxyInstance
生成$Proxy0.class
通过反射调用目标方法

2. CGLIB代理生成流程

定义目标类
创建Enhancer实例
设置superclass和callback
生成FastClass信息
生成目标类的子类
通过方法索引直接调用

四、性能实测数据(参考)

操作类型JDK 17(ns/op)CGLIB 3.3.6(ns/op)
代理创建时间12002500
方法调用耗时4512
内存占用1.2MB2.8MB

测试环境:MacBook Pro M1/16GB,测试循环10000次

五、生产环境选择策略

1. 优先使用场景

  • 选择JDK代理

    • 目标对象已实现接口
    • 需要最小化依赖
    • 代理少量方法(<5个)
  • 选择CGLIB

    • 目标对象无接口
    • 需要代理具体类
    • 代理方法数量多(>=10个)
    • 需要更高性能(如高频调用)

2. Spring框架策略

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AppConfig {
}

默认行为

  • 目标实现接口 -> JDK代理
  • 目标未实现接口 -> CGLIB代理

六、常见问题解决方案

1. JDK代理类型转换异常

// 错误示例
UserServiceImpl impl = (UserServiceImpl) proxy; // ClassCastException

// 正确方式
UserService service = (UserService) proxy;

2. CGLIB代理final方法限制

public class UserService {
    public final void audit() { // 无法被代理
        // ...
    }
}

解决方案

  • 使用组合模式替代继承
  • 重构final方法为非final

3. 代理对象序列化问题

// 需要实现Serializable接口
public class CglibProxy implements MethodInterceptor, Serializable {
    // 添加serialVersionUID
    private static final long serialVersionUID = 1L;
}

七、扩展知识接口

1. 字节码操作框架对比

框架名称生成方式性能易用性功能完整性
ASM直接操作字节码最高最难最完整
Javassist源码级操作中等简单较完整
Byte Buddy链式API最易完整

2. 新型代理技术

  • ByteBuddy:更现代的字节码操作库
  • JEP 416(Java 18):方法句柄代理
  • GraalVM代理:支持Native Image的代理方案

3. 设计模式应用

// 组合代理模式示例
public class HybridProxy implements InvocationHandler {
    private Object target;
    
    public static Object createProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            getAllInterfaces(target),
            new HybridProxy(target)
        );
    }
    
    private static Class<?>[] getAllInterfaces(Object target) {
        // 实现获取所有接口的逻辑
    }
}

建议在项目初期明确代理需求,对于Spring项目推荐优先使用CGLIB以保证灵活性,同时注意代理对象的内存管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Remember_Ray

何其有幸,得你青睐

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值