JDK 动态代理与 CGLIB 代理

92 阅读6分钟

🌟 JDK 动态代理与 CGLIB 代理 🌟


🤔 什么是代理?

在编程中,代理模式 是一种常用的设计模式,它允许你通过 代理对象 来间接访问 目标对象。通过代理对象,你可以在不改变目标对象代码的情况下,给目标方法添加附加功能,比如 日志记录 📝、权限校验 🔐、事务控制 💼 等。

举个例子:

假设你有一个 买电影票 🎬 的系统,但你希望每次买票时记录下日志 📜 或者检查是否有权限 ✅。你不想修改原本的买票代码,这时候代理模式就可以帮你实现这个需求。✨


🏗️ 1. JDK 动态代理 🏗️

JDK 动态代理是什么?

JDK 动态代理基于接口 的代理方式。它要求目标类实现接口,并在运行时通过反射机制 动态 创建一个代理类,拦截接口中的方法。

JDK 动态代理的工作流程 🔄:
  • 目标类实现接口 🧩
    目标类(例如:MovieTicket)必须实现一个接口(例如:TicketService)🎯。

  • 使用 Proxy.newProxyInstance() 创建代理对象 🛠️
    代理对象是 动态生成 的,它会实现目标接口,并通过 InvocationHandler 来拦截方法。

  • 通过 InvocationHandler 拦截方法 🧑💻
    InvocationHandler 接口定义了一个 invoke() 方法,用来在方法调用时插入自定义逻辑📋。

  • 通过代理对象调用目标方法 🎯
    当你调用代理对象的方法时,实际执行的是 invoke() 方法,代理对象会在方法执行前后执行一些额外的逻辑🔄。


JDK 动态代理的代码示例 💻:

1. 定义接口: 🎬
// 定义电影票服务接口
public interface TicketService {
    void buyTicket(String movieName);  // 定义购买电影票的方法
}
2. 实现接口: 💡
// 实现 TicketService 接口的实际类
class MovieTicketImpl implements TicketService {
    @Override
    public void buyTicket(String movieName) {
        // 这是实际的业务逻辑,处理购买电影票
        System.out.println("购买电影票: " + movieName);
    }
}
3. 创建代理对象并实现 InvocationHandler 🛠️
import java.lang.reflect.*;  // 导入反射类

public class JDKProxyDemo {
    public static void main(String[] args) {
        // 创建实际的 MovieTicket 实例
        TicketService realService = new MovieTicketImpl();
        
        // 创建代理对象
        TicketService proxyService = (TicketService) Proxy.newProxyInstance(
            TicketService.class.getClassLoader(),  // 目标类的类加载器
            new Class<?>[]{TicketService.class},   // 目标类实现的接口
            new InvocationHandler() {  // 代理对象的逻辑
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 在方法执行前添加额外的逻辑(如日志、权限检查等)🎯
                    System.out.println("消息处理前 ⏰");

                    // 调用实际方法
                    Object result = method.invoke(realService, args);

                    // 在方法执行后添加额外的逻辑 🕰️
                    System.out.println("消息处理后 🕰️");

                    return result;  // 返回实际方法的结果
                }
            }
        );
    
        // 通过代理对象调用方法
        proxyService.buyTicket("复仇者联盟");
    }
}

代码解析 🧐

  • Proxy.newProxyInstance()
    这个方法用于 动态生成代理类✨。它需要 目标类的类加载器ClassLoader)、目标类实现的接口,以及 InvocationHandler 实现(代理对象的逻辑)🎯。

  • InvocationHandler
    InvocationHandler 是 JDK 动态代理中的核心,它是用来 拦截 方法调用的接口。
    invoke() 方法中,你可以插入自定义逻辑(例如:打印日志 📜、事务控制 💼 等)。然后,通过 method.invoke(realService, args) 调用实际方法。

  • method.invoke(realService, args)
    这行代码是通过 反射 机制执行实际的目标方法🔍。

  • 通过代理对象调用方法 🎯:
    当你调用 proxyService.buyTicket("复仇者联盟") 时,实际上是触发了 invoke() 方法。
    invoke() 会先执行代理逻辑,再调用目标方法,最后返回结果🔄。

输出: 🎬
消息处理前 ⏰
购买电影票: 复仇者联盟
消息处理后 🕰️
总结 📚:
  • JDK 动态代理 通过 接口 动态创建代理类,并用 InvocationHandler 来拦截方法。
  • 你可以在 invoke() 中为目标方法调用前后添加任何自定义逻辑,例如:日志记录 📜、权限校验 ✅ 等。

🧩 CGLIB 代理 🧩

CGLIB 代理是什么?

CGLIB 代理 通过 继承目标类 来创建代理对象。与 JDK 动态代理 不同,CGLIB 不需要目标类实现接口。它通过 子类化 目标类来实现代理,适用于 没有接口 的类。

CGLIB 代理的工作流程 🔄:
  • 目标类无需实现接口
    目标类不需要实现接口,CGLIB 会通过 继承 目标类来生成代理类👨💻。

  • 使用 Enhancer 创建代理对象
    CGLIB 使用 Enhancer 类来创建代理对象,并通过 MethodInterceptor 拦截方法⚡。

  • 方法拦截器 (MethodInterceptor)
    MethodInterceptor 接口定义了 intercept() 方法,我们可以在这个方法中插入自定义的逻辑🧠。

  • 通过代理对象调用目标方法
    当你调用代理对象的方法时,CGLIB 会先执行 intercept() 方法,然后调用实际的目标方法🔄。


CGLIB 代理的代码示例:

定义目标类(无需实现接口): 💡
// 实际的电影票购买类
class MovieTicket {
    public void buyTicket(String movieName) {
        // 购买电影票的业务逻辑
        System.out.println("购买电影票: " + movieName);
    }
}
使用 CGLIB 代理:
import org.springframework.cglib.proxy.*;

public class CGLIBProxyDemo {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        
        // 设置需要代理的类(MovieTicket)
        enhancer.setSuperclass(MovieTicket.class);  // 设置目标类
        
        // 设置方法拦截器:方法执行时会调用这个拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                // 在方法执行前添加自定义的逻辑 🎯
                System.out.println("消息处理前 ⏰");

                // 调用父类的实际方法
                Object result = proxy.invokeSuper(obj, args);

                // 在方法执行后添加自定义的逻辑 🕰️
                System.out.println("消息处理后 🕰️");

                return result;  // 返回父类方法的结果
            }
        });

        // 创建代理对象
        MovieTicket proxyTicket = (MovieTicket) enhancer.create();
        
        // 通过代理对象调用方法 🎬
        proxyTicket.buyTicket("复仇者联盟");
    }
}

代码解析 🧐

  • Enhancer.setSuperclass()
    指定需要代理的目标类(MovieTicket)。CGLIB 会通过继承这个类来创建代理👨💻。

  • MethodInterceptor
    intercept() 方法会在目标方法执行时被调用,我们可以在方法执行前后插入自定义的逻辑。
    proxy.invokeSuper() 调用父类的方法,这是代理执行实际方法的地方。

  • enhancer.create()
    通过 Enhancer.create() 创建代理对象💡。

输出:
消息处理前 ⏰
购买电影票: 复仇者联盟
消息处理后 🕰️
总结 📚:
  • CGLIB 代理 是通过 继承目标类 创建代理对象,适用于目标类没有实现接口的情况。
  • 它通过 MethodInterceptor 拦截方法并执行自定义逻辑。

📝 JDK 动态代理与 CGLIB 代理对比 📊

特性JDK 动态代理 🏗️CGLIB 代理 🧩
代理方式基于 接口 生成代理类基于 继承目标类 生成代理类
目标要求目标类必须实现 接口目标类 不需要实现接口
方法限制只能代理 public 方法无法代理 finalprivate 方法
代理对象生成方式使用 Proxy.newProxyInstance() 创建代理使用 Enhancer 创建代理
适用场景适用于有接口的类,简单且高效适用于没有接口的类,灵活且强大

🎯 总结 🎯

  • JDK 动态代理
    适用于目标类 实现了接口 的情况。
    通过 InvocationHandler 动态生成代理对象,并增强方法调用🛠️。

  • CGLIB 代理
    适用于目标类 没有接口 的情况,继承 目标类并生成代理🧩。
    可以拦截目标类的 普通方法,但无法拦截 finalprivate 方法。

  • 选择依据
    如果目标类有接口,选择 JDK 动态代理🧑💻。
    如果目标类没有接口,选择 CGLIB 代理 🧩。