jdk动态代理和cglib动态代理

要彻底搞懂 JDK 动态代理和 CGLIB,我们先从「代理模式」的核心思想入手,再分别拆解两种动态代理的实现原理、代码示例和核心区别,最后总结适用场景。

一、前置知识:代理模式的核心

代理模式是一种设计模式,核心是通过代理类控制对目标类的访问,可以在不修改目标类代码的前提下,增加额外功能(如日志、事务、权限校验)。

  • 静态代理:代理类在编译期就确定,一对一绑定目标类,灵活性差。
  • 动态代理:代理类在运行期动态生成,无需手动编写代理类,是日常开发(如 Spring AOP)的核心。

JDK 动态代理和 CGLIB 是动态代理的两种主流实现方式,核心区别在于是否依赖接口


二、JDK 动态代理

1. 核心原理

JDK 动态代理是 JDK 自带的功能(无需额外依赖),要求目标类必须实现至少一个接口

  • 底层通过 java.lang.reflect.Proxy 类动态生成代理类字节码,通过 InvocationHandler 接口处理代理逻辑。
  • 生成的代理类会实现目标类的所有接口,并通过反射调用目标方法。

2. 代码示例

步骤1:定义目标接口和目标类

// 目标接口
public interface UserService {
    void addUser(String username);
    void deleteUser(String username);
}

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

    @Override
    public void deleteUser(String username) {
        System.out.println("删除用户:" + username);
    }
}

步骤2:实现 InvocationHandler 接口(核心:代理逻辑)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 自定义调用处理器,封装代理逻辑(如日志)
public class JdkProxyHandler implements InvocationHandler {
    // 目标对象(被代理的对象)
    private Object target;

    // 构造器注入目标对象
    public JdkProxyHandler(Object target) {
        this.target = target;
    }

    /**
     * 核心方法:代理类调用方法时,会触发此方法
     * @param proxy 生成的代理对象(一般不用)
     * @param method 目标方法
     * @param args 目标方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强:调用目标方法前添加日志
        System.out.println("[JDK代理] 方法 " + method.getName() + " 开始执行,参数:" + args[0]);
        
        // 调用目标类的原始方法
        Object result = method.invoke(target, args);
        
        // 后置增强:调用目标方法后添加日志
        System.out.println("[JDK代理] 方法 " + method.getName() + " 执行完成");
        return result;
    }
}

步骤3:生成代理对象并测试

import java.lang.reflect.Proxy;

public class JdkProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        UserService target = new UserServiceImpl();
        
        // 2. 创建调用处理器
        JdkProxyHandler handler = new JdkProxyHandler(target);
        
        // 3. 动态生成代理对象(核心方法:Proxy.newProxyInstance)
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 目标类的类加载器
                target.getClass().getInterfaces(),   // 目标类实现的接口(必须)
                handler                              // 调用处理器
        );
        
        // 4. 调用代理对象的方法(实际执行 handler.invoke 方法)
        proxy.addUser("张三");
        proxy.deleteUser("李四");
    }
}

输出结果

[JDK代理] 方法 addUser 开始执行,参数:张三
添加用户:张三
[JDK代理] 方法 addUser 执行完成
[JDK代理] 方法 deleteUser 开始执行,参数:李四
删除用户:李四
[JDK代理] 方法 deleteUser 执行完成

3. 关键要点

  • 必须依赖接口,否则会抛出 IllegalArgumentException
  • 代理类由 Proxy 类动态生成,类名格式为 $Proxy0(运行期可见)。
  • 底层基于反射,调用效率略低于 CGLIB。

三、CGLIB 动态代理

1. 核心原理

CGLIB(Code Generation Library)是第三方库(Spring 已内置),无需目标类实现接口,通过「继承」目标类动态生成子类作为代理类。

  • 底层通过 ASM 字节码框架修改字节码,生成目标类的子类。
  • 重写目标类的非 final 方法,植入代理逻辑。

2. 代码示例

前置依赖(若未使用 Spring,需手动引入):

<!-- Maven 依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

步骤1:定义目标类(无需实现接口)

// 目标类(无接口)
public class OrderService {
    public void createOrder(String orderNo) {
        System.out.println("创建订单:" + orderNo);
    }

    public void cancelOrder(String orderNo) {
        System.out.println("取消订单:" + orderNo);
    }
}

步骤2:实现 MethodInterceptor 接口(核心:代理逻辑)

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// CGLIB 方法拦截器,封装代理逻辑
public class CglibProxyInterceptor implements MethodInterceptor {
    // 目标对象
    private Object target;

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

    /**
     * 核心方法:代理类调用方法时触发
     * @param obj 代理对象(子类)
     * @param method 目标方法
     * @param args 方法参数
     * @param proxy 方法代理对象(用于调用父类方法)
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强
        System.out.println("[CGLIB代理] 方法 " + method.getName() + " 开始执行,参数:" + args[0]);
        
        // 调用目标类的原始方法(通过 MethodProxy 调用,效率更高)
        Object result = proxy.invokeSuper(obj, args);
        
        // 后置增强
        System.out.println("[CGLIB代理] 方法 " + method.getName() + " 执行完成");
        return result;
    }
}

步骤3:生成代理对象并测试

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderService();
        
        // 2. 创建 CGLIB 增强器(核心类)
        Enhancer enhancer = new Enhancer();
        // 设置父类(目标类)
        enhancer.setSuperclass(OrderService.class);
        // 设置方法拦截器
        enhancer.setCallback(new CglibProxyInterceptor(target));
        
        // 3. 生成代理对象(子类)
        OrderService proxy = (OrderService) enhancer.create();
        
        // 4. 调用代理方法
        proxy.createOrder("ORDER_001");
        proxy.cancelOrder("ORDER_001");
    }
}

输出结果

[CGLIB代理] 方法 createOrder 开始执行,参数:ORDER_001
创建订单:ORDER_001
[CGLIB代理] 方法 createOrder 执行完成
[CGLIB代理] 方法 cancelOrder 开始执行,参数:ORDER_001
取消订单:ORDER_001
[CGLIB代理] 方法 cancelOrder 执行完成

3. 关键要点

  • 无需接口,通过继承实现,因此目标类不能是 final 类,目标方法不能是 final 方法(final 方法无法被重写)。
  • 基于字节码生成,调用效率比 JDK 动态代理高(JDK 1.8 后差距缩小)。
  • 代理类是目标类的子类,因此无法代理私有方法(私有方法无法被继承)。

四、JDK 动态代理 vs CGLIB 核心对比

特性 JDK 动态代理 CGLIB 动态代理
依赖条件 目标类必须实现接口 无需接口,依赖继承
底层实现 Java 反射 API ASM 字节码框架
代理类关系 实现目标接口(兄弟关系) 继承目标类(父子关系)
限制 无接口则无法使用 目标类/方法不能是 final
效率(JDK 1.8+) 反射调用,效率略低 字节码直接生成,效率略高
适用场景 目标类有接口(如 Spring Bean 实现接口) 目标类无接口(如普通 POJO)

五、Spring AOP 中的应用

Spring AOP 默认采用「JDK 动态代理」,但如果目标类没有实现接口,则自动切换为「CGLIB」。

  • 可以通过配置 proxy-target-class=true 强制使用 CGLIB。
  • Spring 5.x 后,CGLIB 已内置,无需手动引入依赖。

总结

  1. JDK 动态代理:基于接口实现,JDK 自带无需依赖,通过 InvocationHandlerProxy 生成代理,适合有接口的场景。
  2. CGLIB 动态代理:基于继承实现,无需接口,通过 MethodInterceptorEnhancer 生成代理子类,适合无接口的场景,但目标类/方法不能是 final。
  3. 核心选择:有接口用 JDK 代理,无接口用 CGLIB;Spring AOP 会自动适配,无需手动选择。
posted @ 2026-03-12 09:48  七星6609  阅读(3)  评论(0)    收藏  举报