【Java】动态代理

动态代理(Dynamic Proxy)是一种在程序运行时动态生成代理对象的技术,用于在不修改原始类代码的前提下,拦截并增强目标对象的方法调用。其核心价值在于实现逻辑与业务解耦,广泛应用于日志记录、事务管理、权限控制等场景。


⚙️ 核心概念与原理

  1. 代理的本质
    • 静态代理:需预先编写代理类,每个目标类需对应一个代理类,代码冗余度高。
    • 动态代理:在运行时通过反射或字节码技术(如JDK Proxy、CGLIB)动态生成代理类,无需手动编码。
  2. 工作原理
    • 拦截机制:代理对象拦截所有方法调用,转发给关联的处理器(如InvocationHandlerMethodInterceptor)。
    • 增强逻辑:在目标方法执行前后插入自定义操作(如日志记录、权限校验)。

🛠️ 实现方式(以Java为例)

JDK动态代理(基于接口)

  • 要求:目标类必须实现至少一个接口。
  • 核心类:
    • Proxy:生成代理对象(Proxy.newProxyInstance())。
    • InvocationHandler:定义拦截逻辑(invoke()方法)。
  • 示例:
    public class LogHandler implements InvocationHandler {
        private Object target;
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before method: " + method.getName());
            Object result = method.invoke(target, args);  // 调用目标方法
            System.out.println("After method: " + method.getName());
            return result;
        }
    }
    // 生成代理对象
    Service proxy = (Service) Proxy.newProxyInstance(
        target.getClass().getClassLoader(),
        target.getClass().getInterfaces(),
        new LogHandler(target)
    );
    

CGLIB动态代理(基于继承)

  • 要求:可代理普通类(无法代理final类或方法)。
  • 核心类:
    • Enhancer:生成代理类(继承目标类)。
    • MethodInterceptor:定义拦截逻辑(intercept()方法)。
  • 示例:
    public class CglibInterceptor implements MethodInterceptor {
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before method: " + method.getName());
            Object result = proxy.invokeSuper(obj, args);  // 调用父类(目标类)方法
            System.out.println("After method: " + method.getName());
            return result;
        }
    }
    // 生成代理对象
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(TargetClass.class);
    enhancer.setCallback(new CglibInterceptor());
    TargetClass proxy = (TargetClass) enhancer.create();
    

JDK代理 vs CGLIB代理对比

维度JDK动态代理CGLIB动态代理
代理对象要求必须实现接口可代理任意普通类(非final
实现机制反射字节码增强(ASM框架)
性能调用速度较慢(反射开销)生成快,调用快(直接调用)
依赖Java原生支持需引入CGLIB库

典型应用场景

  1. 面向切面编程(AOP)
    • 日志记录:在方法调用前后自动记录参数、返回值。
    • 事务管理:方法执行前开启事务,执行后提交或回滚(如Spring @Transactional)。
    • 权限控制:拦截方法调用,校验用户权限。
  2. 框架集成
    • Spring AOP:默认使用JDK代理(接口)或CGLIB(无接口类)实现切面。
    • MyBatis:动态代理Mapper接口,自动生成SQL执行逻辑。
    • RPC框架(如Dubbo):代理远程服务接口,封装网络通信细节。
  3. 性能优化
    • 延迟加载:代理对象按需加载资源(如Hibernate懒加载)。
    • 缓存管理:方法调用前查询缓存s,命中则直接返回,避免重复计算。

⚖️ 优缺点分析

优点缺点
无侵入性:不修改目标类代码,实现功能增强。性能开销:反射或字节码生成导致调用速度略低于直接调用。
解耦设计:分离核心业务与横切关注点(如日志、事务)。使用限制:JDK代理需接口,CGLIB无法代理final方法。
灵活扩展:动态调整代理逻辑,适应多变需求。复杂度:代理链过长可能增加调试难度。

💎 总结

动态代理是运行时动态生成代理对象的核心技术,通过拦截方法调用实现功能增强,其价值在于:

  1. 解耦业务与非功能性需求(如日志、事务),提升代码可维护性;
  2. 支撑主流框架(如Spring AOP、MyBatis),简化开发流程;
  3. 扩展性强,可灵活适应复杂业务场景。

⚠️ 使用建议:优先选择JDK代理(目标类有接口),无接口时用CGLIB;避免过度代理导致性能下降,可通过缓存代理类优化效率。

Licensed under CC BY-NC-SA 4.0
Last updated on Jul 17, 2025 09:26 CST
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy