动态代理(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高级特性和框架设计的重要基础,也是面试中的高频考点。

相关推荐
猷咪17 小时前
C++基础
开发语言·c++
IT·小灰灰17 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧17 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q17 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳017 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾17 小时前
php 对接deepseek
android·开发语言·php
vx_BS8133017 小时前
【直接可用源码免费送】计算机毕业设计精选项目03574基于Python的网上商城管理系统设计与实现:Java/PHP/Python/C#小程序、单片机、成品+文档源码支持定制
java·python·课程设计
2601_9498683617 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
gzxx2007sddx17 小时前
windows vnpy运行过程及问题记录
python·量化·vnpy
星火开发设计18 小时前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识