【Java】深入理解 Java 动态代理:JDK 与 CGLIB 的原理、区别及实战示例

Java中JDK动态代理与CGLIB动态代理的区别详解

在Java开发中,动态代理是一种非常重要的设计模式,广泛应用于AOP(面向切面编程)、事务管理、日志记录等场景。

Java生态中主要有两种动态代理实现方式:JDK动态代理和CGLIB动态代理。本文将从原理、使用方式、性能、适用场景等多个维度详细对比这两种代理机制,并附上完整的代码示例。

一、什么是动态代理?

动态代理是指在程序运行时动态生成代理对象,而不需要在编译期就确定代理类。代理对象可以拦截对目标对象方法的调用,在方法执行前后插入自定义逻辑(如日志、权限校验、事务控制等)。

二、JDK动态代理

2.1 原理

  • JDK动态代理基于 Java反射机制 实现。
  • 要求被代理的类必须实现至少一个 接口(Interface)。
  • 代理类在运行时通过java.lang.reflect.Proxy类动态生成,该代理类会实现目标对象所实现的所有接口。
  • 方法调用通过InvocationHandler 的 invoke()方法进行拦截。

2.2 优点

  • 纯Java实现,无需引入第三方库。
  • 代理逻辑清晰,易于理解和调试。

2.3 缺点

  • 只能代理实现了接口的类,无法代理没有接口的类或 final 类。

2.4 代码示例

// 1. 定义接口
public interface UserService {
    void addUser(String name);
    String getUser(String id);
}

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

    @Override
    public String getUser(String id) {
        System.out.println("查询用户ID: " + id);
        return "User-" + id;
    }
}

// 3. 自定义 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("【JDK代理】方法调用前: " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("【JDK代理】方法调用后");
        return result;
    }
}

// 4. 测试类
import java.lang.reflect.Proxy;

public class JdkProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        MyInvocationHandler handler = new MyInvocationHandler(userService);

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            userService.getClass().getClassLoader(),
            userService.getClass().getInterfaces(),
            handler
        );

        proxy.addUser("张三");
        String user = proxy.getUser("1001");
        System.out.println("返回结果: " + user);
    }
}

输出结果:

【JDK代理】方法调用前: addUser
添加用户: 张三
【JDK代理】方法调用后
【JDK代理】方法调用前: getUser
查询用户ID: 1001
【JDK代理】方法调用后
返回结果: User-1001

三、CGLIB动态代理

3.1 原理

  • CGLIB(Code Generation Library)是一个强大的高性能代码生成库。
  • 它通过 继承目标类 并重写其非 final 方法来实现代理。
  • 底层使用 ASM 字节码操作框架,在运行时动态生成子类(代理类)。
  • 不要求目标类实现接口。

3.2 优点

  • 可以代理 没有实现接口的类。
  • 性能通常优于 JDK 动态代理(尤其在 JDK 8 之前)。

3.3 缺点

  • 无法代理 final 类 或 final 方法(因为不能被继承/重写)。
  • 需要额外引入 CGLIB 依赖(虽然 Spring 等框架已内置)。
  • 由于是通过继承实现,可能引发一些设计上的问题(如构造器多次调用)。

3.4 Maven 依赖(如未使用 Spring)

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

注:Spring Boot 项目通常已包含 CGLIB,无需单独引入。

3.5 代码示例

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

    public final void finalizeOrder(String orderId) {
        System.out.println("最终确认订单(final方法): " + orderId);
    }
}

// 2. 自定义 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("【CGLIB代理】调用方法前: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类方法
        System.out.println("【CGLIB代理】调用方法后");
        return result;
    }
}

// 3. 测试类
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class);
        enhancer.setCallback(new CglibMethodInterceptor());

        OrderService proxy = (OrderService) enhancer.create();

        proxy.createOrder("ORD-2025");
        proxy.finalizeOrder("ORD-2025"); // 注意:final 方法不会被代理!
    }
}

输出结果:

【CGLIB代理】调用方法前: createOrder
创建订单: ORD-2025
【CGLIB代理】调用方法后
最终确认订单(final方法): ORD-2025

⚠️ 注意:finalizeOrder 是 final 方法,CGLIB 无法代理,因此不会触发拦截逻辑。

四、JDK动态代理 vs CGLIB动态代理 对比总结

特性JDK动态代理CGLIB动态代理
实现原理基于接口 + 反射基于继承 + 字节码生成(ASM)
是否需要接口✅ 必须实现接口❌ 不需要接口
能否代理 final 类/方法❌ 不能(但接口无此问题)❌ 不能代理 final 类或 final 方法
性能(JDK 8+)较高(反射优化)略高或相当(早期版本 CGLIB 更快)
依赖JDK 自带需引入 cglib.jar
代理对象类型接口类型目标类的子类
适用场景目标类有接口(如 Spring 中的 Service 接口)目标类无接口(如某些工具类、遗留代码)

📌 Spring 框架中的选择策略:

  • 如果目标 Bean 实现了接口,Spring 默认使用 JDK 动态代理。
  • 如果目标 Bean 没有实现接口,Spring 自动切换为 CGLIB 代理。
  • 可通过配置强制使用 CGLIB:@EnableAspectJAutoProxy(proxyTargetClass = true)

五、结语

理解 JDK 动态代理与 CGLIB 动态代理的区别,有助于我们在实际开发中做出合理的技术选型,尤其是在使用 Spring AOP、MyBatis 等框架时。掌握其底层原理,也能帮助我们更好地排查代理相关的问题(如代理失效、方法未被拦截等)。

希望本文能帮助你深入理解 Java 动态代理机制!欢迎关注我的 优快云 博客,获取更多高质量技术文章!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值