动态代理(JDK动态代理/CGLIB动态代理

动态代理核心概念

动态代理是一种运行时动态生成代理类 的机制,用于增强目标对象的方法 (如添加日志、事务、权限控制等),而无需修改目标对象的源码。Java中主要有两种动态代理实现:JDK动态代理CGLIB动态代理

一、JDK动态代理实现原理

JDK动态代理是基于接口 的动态代理,核心依赖java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler接口。

1. 实现条件
  • 目标类必须实现至少一个接口
  • 代理类是运行时动态生成 的,继承自Proxy类,实现目标类的所有接口
2. 核心组件
  • Proxy :用于生成代理实例,核心方法newProxyInstance()
  • InvocationHandler接口 :代理类的方法调用处理器,通过invoke()方法增强目标方法
3. 实现流程
  1. 目标类实现接口
  2. 创建InvocationHandler实现类,重写invoke()方法
  3. 通过Proxy.newProxyInstance()生成代理实例
  4. 调用代理实例的方法,最终会转发到InvocationHandler.invoke()方法
4. 代码示例
java 复制代码
// 1. 定义接口
interface UserService {
    void addUser(String name);
}

// 2. 目标类实现接口
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

// 3. 实现InvocationHandler
class LogInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    // 代理方法调用时触发,proxy:代理实例,method:目标方法,args:方法参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增强逻辑:前置日志
        System.out.println("开始调用方法:" + method.getName());
        // 调用目标方法
        Object result = method.invoke(target, args);
        // 增强逻辑:后置日志
        System.out.println("方法调用结束:" + method.getName());
        return result;
    }
}

// 4. 生成代理实例并调用
public class JDKProxyDemo {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        // 生成代理实例
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 类加载器
                target.getClass().getInterfaces(),   // 目标类实现的接口
                new LogInvocationHandler(target)      // 调用处理器
        );
        // 调用代理方法
        proxy.addUser("张三");
    }
}
5. 代理类生成机制
  • JDK在运行时通过字节码生成技术动态生成代理类的字节码
  • 生成的代理类继承自Proxy,实现目标接口
  • 代理类的每个方法都会调用InvocationHandler.invoke()方法
二、CGLIB动态代理实现原理

CGLIB(Code Generation Library)是基于继承的动态代理,通过继承目标类并重写其方法来实现增强。

1. 实现条件
  • 目标类无需实现接口
  • 目标类不能是final类(无法继承)
  • 目标方法不能是finalstatic(无法重写)
2. 核心组件
  • Enhancer :用于生成代理实例,核心方法create()
  • MethodInterceptor接口 :方法拦截器,通过intercept()方法增强目标方法
3. 实现流程
  1. 引入CGLIB依赖
  2. 创建MethodInterceptor实现类,重写intercept()方法
  3. 通过Enhancer.create()生成代理实例
  4. 调用代理实例的方法,最终会转发到MethodInterceptor.intercept()方法
4. 代码示例
xml 复制代码
<!-- Maven依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
java 复制代码
// 1. 目标类(无需实现接口)
class UserService {
    public void addUser(String name) {
        System.out.println("添加用户:" + name);
    }
}

// 2. 实现MethodInterceptor
class LogMethodInterceptor implements MethodInterceptor {
    // o:代理实例,method:目标方法,args:方法参数,methodProxy:代理方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 增强逻辑:前置日志
        System.out.println("开始调用方法:" + method.getName());
        // 调用目标方法(两种方式)
        // 方式1:调用目标类的原始方法(可能触发递归)
        // Object result = method.invoke(o, args);
        // 方式2:调用代理类的fastInvoke方法(推荐,性能更高)
        Object result = methodProxy.invokeSuper(o, args);
        // 增强逻辑:后置日志
        System.out.println("方法调用结束:" + method.getName());
        return result;
    }
}

// 3. 生成代理实例并调用
public class CGLIBProxyDemo {
    public static void main(String[] args) {
        // 生成代理实例
        UserService proxy = (UserService) Enhancer.create(
                UserService.class,                 // 目标类Class
                new LogMethodInterceptor()         // 方法拦截器
        );
        // 调用代理方法
        proxy.addUser("李四");
    }
}
5. 代理类生成机制
  • CGLIB在运行时通过ASM字节码操作库动态生成代理类的字节码
  • 生成的代理类继承自目标类,重写目标类的非final方法
  • 代理类的每个方法都会调用MethodInterceptor.intercept()方法
三、JDK动态代理与CGLIB动态代理的详细对比
特性 JDK动态代理 CGLIB动态代理
实现方式 基于接口,代理类实现目标接口 基于继承,代理类继承目标类
目标类要求 必须实现至少一个接口 无需实现接口,但不能是final
代理类生成 继承自java.lang.reflect.Proxy 继承自目标类
核心API Proxy.newProxyInstance()InvocationHandler Enhancer.create()MethodInterceptor
方法增强范围 仅增强接口中定义的方法 增强目标类的所有非final/非static方法
final方法支持 不涉及(接口方法不能是final) 不支持(final方法无法重写)
性能对比 - JDK 8以前:性能较差 - JDK 8+:性能优于CGLIB(JDK优化了动态代理实现) - 创建代理时:性能较差(需要生成字节码) - 运行时:方法调用性能与JDK接近
生成代理速度 快(JDK原生支持) 慢(需要ASM生成字节码)
内存占用 较低 较高(生成的代理类字节码较大)
适用场景 目标类实现了接口的场景 目标类未实现接口的场景
Spring AOP默认选择 目标类实现接口时,优先使用JDK动态代理(Spring 5.x+) 目标类未实现接口时,使用CGLIB
四、代理选择原则
  1. 目标类是否实现接口
    • 实现接口 → 优先使用JDK动态代理(JDK 8+性能更好)
    • 未实现接口 → 使用CGLIB动态代理
  2. 性能需求
    • 频繁创建代理 → 优先使用JDK动态代理(创建速度快)
    • 频繁调用代理方法 → JDK 8+使用JDK动态代理 ,JDK 7-使用CGLIB
  3. 特殊需求
    • 需要增强final方法 → 无法使用动态代理,需考虑其他方案(如AspectJ编译时织入)
    • 需要代理static方法 → 动态代理不支持,需考虑其他方案
五、动态代理的常见应用场景
  1. Spring AOP:核心实现基于动态代理,根据目标类是否实现接口选择JDK或CGLIB
  2. 事务管理:通过代理增强方法,自动添加事务的开始、提交、回滚逻辑
  3. 日志记录:统一记录方法的调用日志、执行时间等
  4. 权限控制:在方法调用前检查用户权限
  5. 远程方法调用(RPC):如Dubbo、Feign,通过代理实现远程调用的透明化
  6. Mock框架:如Mockito,通过动态代理模拟对象行为

总结

JDK动态代理和CGLIB动态代理是Java中实现动态代理的两种主要方式,它们的核心区别在于实现机制 (接口vs继承)和目标类要求。在实际开发中,应根据目标类的特点和性能需求选择合适的代理方式,大多数框架(如Spring AOP)会自动根据目标类的情况选择最优方案。

理解两种动态代理的实现原理和区别,是掌握Java高级特性和框架设计的重要基础,也是面试中的高频考点。

相关推荐
laocooon5238578862 小时前
背包问题~~!C++
开发语言·c++·算法
CreasyChan2 小时前
C# 异步编程详解
开发语言·windows·c#
破烂pan3 小时前
Python 整合 Redis 哨兵(Sentinel)与集群(Cluster)实战指南
redis·python·sentinel
悟能不能悟3 小时前
java 判断string[]中是否有a
java·开发语言
4***14903 小时前
高并发时代的“确定性”挑战——为何稳定性正在成为 JVM 的下一场核心竞争?
java·开发语言·jvm
程序员杰哥3 小时前
接口测试之文件上传
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
2401_841495643 小时前
【自然语言处理】单字与双字字频统计算法设计
人工智能·python·算法·自然语言处理·单字·双字·字频统计
fegggye3 小时前
创建一个rust写的python库[signatures和错误处理]
开发语言·python·rust
hahjee3 小时前
Go编写的ANSI终端颜色和样式控制库在OpenHarmony PC上的完整适配实战
开发语言·后端·鸿蒙