一文深入学习Java动态代理-JDK动态代理和CGLIB

在Java中,动态代理是一种运行时动态生成代理对象的技术,属于代理模式的进阶实现。它无需像静态代理那样手动编写代理类,而是通过框架在运行时自动生成代理类字节码,从而简化代理逻辑的实现,提升代码灵活性。

一、代理模式的核心思想

代理模式的核心是:通过代理对象间接访问目标对象,在不修改目标对象代码的前提下,为目标对象的方法添加额外功能(如日志、事务、权限校验等)

  • 目标对象:被代理的原始对象(如业务逻辑类)。
  • 代理对象:持有目标对象的引用,对外提供与目标对象相同的方法,但在方法执行前后可添加增强逻辑。

二、静态代理的局限

静态代理是代理模式的基础实现,需要手动为每个目标类编写代理类(代理类与目标类实现相同接口)。但它存在明显局限:

  • 代理类与目标类强耦合,若接口新增方法,所有代理类都需同步修改,维护成本高。
  • 当目标类数量多且接口方法复杂时,会产生大量重复的代理代码。

例如,若有100个业务类,静态代理需要编写100个对应的代理类,显然不现实。动态代理正是为解决这一问题而生。

三、Java动态代理的两种实现方式

Java中动态代理的主流实现有两种:JDK动态代理 (基于接口)和CGLIB动态代理(基于继承)。

1. JDK动态代理(基于接口)

JDK动态代理是Java原生支持的代理方式,核心依赖java.lang.reflect包下的两个类:Proxy(生成代理对象)和InvocationHandler(定义代理逻辑)。

核心要求:目标类必须实现至少一个接口(代理对象会实现相同接口)。

实现步骤:

以"为用户服务类添加日志增强"为例:

  • Step 1:定义接口(目标类需实现)

    接口中声明目标方法(代理对象会实现这些方法)。

    java 复制代码
    // 用户服务接口
    public interface UserService {
        void addUser(String name);
    }
  • Step 2:实现目标类

    目标类是实际业务逻辑的载体,需实现上述接口。

    java 复制代码
    // 目标类(被代理的类)
    public class UserServiceImpl implements UserService {
        @Override
        public void addUser(String name) {
            System.out.println("执行核心业务:添加用户 " + name);
        }
    }
  • Step 3:定义代理逻辑(实现InvocationHandler)
    InvocationHandler是一个函数式接口,其中的invoke方法是代理逻辑的核心:当调用代理对象的方法时,会自动触发invoke方法执行。

    java 复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    // 代理逻辑处理器
    public class LogInvocationHandler implements InvocationHandler {
        // 持有目标对象的引用
        private Object target;
    
        public LogInvocationHandler(Object target) {
            this.target = target;
        }
    
        /**
         * 代理逻辑核心方法
         * @param proxy  代理对象(一般不用)
         * @param method 目标方法(被调用的方法)
         * @param args   目标方法的参数
         * @return 目标方法的返回值
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 增强逻辑:方法执行前
            System.out.println("【日志】方法 " + method.getName() + " 开始执行,参数:" + args[0]);
    
            // 调用目标对象的方法(核心业务)
            Object result = method.invoke(target, args);
    
            // 增强逻辑:方法执行后
            System.out.println("【日志】方法 " + method.getName() + " 执行结束");
    
            return result;
        }
    }
  • Step 4:生成代理对象(通过Proxy类)

    使用Proxy.newProxyInstance()方法动态生成代理对象,需要3个参数:

    • 类加载器(与目标类相同);
    • 目标类实现的接口数组;
    • 上述InvocationHandler实例(代理逻辑)。
    java 复制代码
    import java.lang.reflect.Proxy;
    
    public class Main {
        public static void main(String[] args) {
            // 1. 创建目标对象
            UserService target = new UserServiceImpl();
    
            // 2. 创建代理逻辑处理器(传入目标对象)
            LogInvocationHandler handler = new LogInvocationHandler(target);
    
            // 3. 动态生成代理对象(代理对象实现UserService接口)
            UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 类加载器
                target.getClass().getInterfaces(),   // 目标类实现的接口
                handler                              // 代理逻辑
            );
    
            // 4. 调用代理对象的方法(会触发invoke方法)
            proxy.addUser("张三");
        }
    }

执行结果

复制代码
【日志】方法 addUser 开始执行,参数:张三
执行核心业务:添加用户 张三
【日志】方法 addUser 执行结束
2. CGLIB动态代理(基于继承)

CGLIB(Code Generation Library)是一个第三方字节码生成库,它通过继承目标类 生成代理对象(无需目标类实现接口)。核心依赖net.sf.cglib包下的MethodInterceptor接口(定义代理逻辑)和Enhancer类(生成代理对象)。

核心要求 :目标类不能是final(否则无法继承),目标方法不能是final(否则无法重写增强)。

实现步骤:

同样以"为用户服务类添加日志增强"为例(目标类无需实现接口):

  • Step 1:引入CGLIB依赖

    若使用Maven,需在pom.xml中添加:

    xml 复制代码
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
  • Step 2:定义目标类(无需实现接口)

    java 复制代码
    // 目标类(被代理的类,无需实现接口)
    public class UserService {
        public void addUser(String name) {
            System.out.println("执行核心业务:添加用户 " + name);
        }
    }
  • Step 3:定义代理逻辑(实现MethodInterceptor)
    MethodInterceptor接口中的intercept方法是代理逻辑核心:调用代理对象的方法时,会触发该方法。

    java 复制代码
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    // CGLIB代理逻辑处理器
    public class LogMethodInterceptor implements MethodInterceptor {
        /**
         * 代理逻辑核心方法
         * @param obj       代理对象
         * @param method    目标方法
         * @param args      目标方法参数
         * @param proxy     方法代理(用于调用目标方法)
         * @return 目标方法返回值
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            // 增强逻辑:方法执行前
            System.out.println("【日志】方法 " + method.getName() + " 开始执行,参数:" + args[0]);
    
            // 调用目标对象的方法(核心业务)
            Object result = proxy.invokeSuper(obj, args);  // 注意:通过methodProxy调用父类(目标类)方法
    
            // 增强逻辑:方法执行后
            System.out.println("【日志】方法 " + method.getName() + " 执行结束");
    
            return result;
        }
    }
  • Step 4:生成代理对象(通过Enhancer类)
    Enhancer是CGLIB的核心类,用于生成代理对象,需指定父类(目标类)和回调(代理逻辑)。

    java 复制代码
    import net.sf.cglib.proxy.Enhancer;
    
    public class Main {
        public static void main(String[] args) {
            // 1. 创建增强器
            Enhancer enhancer = new Enhancer();
    
            // 2. 设置父类(目标类,代理类会继承该类)
            enhancer.setSuperclass(UserService.class);
    
            // 3. 设置回调(代理逻辑)
            enhancer.setCallback(new LogMethodInterceptor());
    
            // 4. 生成代理对象(代理对象是UserService的子类)
            UserService proxy = (UserService) enhancer.create();
    
            // 5. 调用代理对象的方法(会触发intercept方法)
            proxy.addUser("李四");
        }
    }

执行结果

复制代码
【日志】方法 addUser 开始执行,参数:李四
执行核心业务:添加用户 李四
【日志】方法 addUser 执行结束

四、JDK动态代理 vs CGLIB动态代理

维度 JDK动态代理 CGLIB动态代理
底层原理 基于接口实现(代理类实现目标接口) 基于继承实现(代理类继承目标类)
目标类要求 必须实现至少一个接口 不能是final类,方法不能是final
性能(JDK8+) 生成代理对象快,运行时性能略优 生成代理对象慢(需生成字节码),运行时性能略差(差异极小)
依赖 Java原生支持,无需额外依赖 需引入CGLIB库

五、动态代理的典型应用场景

动态代理是许多框架的核心技术,例如:

  • Spring AOP:通过动态代理实现切面逻辑(如事务、日志)与业务逻辑的分离(默认对接口用JDK代理,对类用CGLIB)。
  • RPC框架:通过动态代理生成远程服务的本地代理对象,屏蔽网络通信细节。
  • 权限控制:在方法调用前通过代理校验用户权限。
  • 日志记录:在方法执行前后自动记录日志。

总结

动态代理的核心价值是**"运行时动态生成代理对象,在不侵入目标类的前提下增强方法逻辑"**。JDK动态代理基于接口,适用于目标类有接口的场景;CGLIB基于继承,适用于目标类无接口的场景。两者各有优劣,实际开发中可根据目标类特性选择。

相关推荐
四念处茫茫5 小时前
Rust:与JSON、TOML等格式的集成
java·rust·json
微知语5 小时前
Cell 与 RefCell:Rust 内部可变性的双生子解析
java·前端·rust
lsnm5 小时前
C++新手项目-JsonRPC框架
开发语言·c++·1024程序员节
晨陌y5 小时前
从 0 到 1 开发 Rust 分布式日志服务:高吞吐设计 + 存储优化,支撑千万级日志采集
开发语言·分布式·rust
雨过天晴而后无语5 小时前
Windchill10+html使用Lightbox轻量化wizard的配置
java·前端·html
Yeniden6 小时前
设计模式>原型模式大白话讲解:就像复印机,拿个原件一复印,就得到一模一样的新东西
java·设计模式·原型模式·1024程序员节
微信api接口介绍6 小时前
微信个人发消息api
运维·服务器·开发语言·前端·网络·微信·ipad
披着羊皮不是狼6 小时前
HTTP 与 API 入门:理解前后端交互原理
java·网络协议·http·交互
小二·6 小时前
仓颉语言中Channel通道的深度解析:从原理到高并发实践
开发语言