🌟 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 方法 | 无法代理 final、private 方法 |
| 代理对象生成方式 | 使用 Proxy.newProxyInstance() 创建代理 | 使用 Enhancer 创建代理 |
| 适用场景 | 适用于有接口的类,简单且高效 | 适用于没有接口的类,灵活且强大 |
🎯 总结 🎯
-
JDK 动态代理:
适用于目标类 实现了接口 的情况。
通过InvocationHandler动态生成代理对象,并增强方法调用🛠️。 -
CGLIB 代理:
适用于目标类 没有接口 的情况,继承 目标类并生成代理🧩。
可以拦截目标类的 普通方法,但无法拦截final和private方法。 -
选择依据:
如果目标类有接口,选择 JDK 动态代理🧑💻。
如果目标类没有接口,选择 CGLIB 代理 🧩。