- 什么是动态代理? (铺垫,理解为什么需要它)
- JDK 动态代理简介 (作为对比,理解其局限性)
- CGLIB 动态代理详解 (核心内容)
- 是什么?工作原理?
- 如何使用?
- 与 JDK 动态代理的核心区别
- CGLIB 的高级特性
MethodInterceptor(核心拦截器)Enhancer(核心类)- 回调过滤器 (
CallbackFilter)
- CGLIB 的注意事项
- 性能
- 构造器问题
final方法/类
- 如何选择?
什么是动态代理?
在 Java 中,代理是一种设计模式,它提供了一个与真实对象(被代理对象)具有相同接口的代理对象,客户端可以通过这个代理对象来访问真实对象,代理对象可以在调用真实对象的方法前后,执行一些额外的操作,
- 日志记录
- 权限控制
- 事务管理
- 性能监控
动态代理与静态代理(我们自己手动编写代理类)不同,它是在程序运行时,通过反射机制动态地创建代理类及其对象,这大大提高了代码的灵活性和可维护性。
JDK 动态代理简介
JDK 自带了一个动态代理实现,它是最基础、最常用的动态代理方式。
核心特点:
- 基于接口:JDK 动态代理要求被代理的对象必须实现一个或多个接口,代理类会实现这些相同的接口。
- 核心类:
java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。 - 工作方式:我们创建一个
InvocationHandler的实现类,然后通过Proxy.newProxyInstance()方法,传入被代理对象的类加载器、接口列表和我们实现的InvocationHandler,来动态生成一个代理对象。
局限性:
如果我们的目标类没有实现任何接口,JDK 动态代理就无能为力了,对于第三方库中的类(如 HashMap)或者我们自己写的普通 POJO 类,就无法使用 JDK 动态代理。
这就引出了我们的主角:CGLIB。
CGLIB 动态代理详解
1 是什么?工作原理?
CGLIB (Code Generation Library) 是一个强大的、高性能的代码生成库,它可以在运行时动态地生成一个子类,并覆盖其非 final 的方法来实现代理。
核心特点:
- 基于类:CGLIB 不要求目标类实现任何接口,它是通过继承来创建代理对象的。
- 工作原理:它通过生成一个目标类的子类,然后将这个子类作为代理类,在这个子类中,它会重写父类(目标类)的非
final和非private方法,当调用代理对象的方法时,实际上会调用到 CGLIB 提供的回调方法(MethodInterceptor),我们可以在回调方法中插入自己的逻辑,并决定是否以及如何调用父类(目标类)的原方法。
类比:你可以把 CGLIB 想象成一个“魔法工厂”,你给它一个“图纸”(目标类),它能瞬间给你造出一个“升级版”(代理子类),这个升级版和原版长得一模一样,但你可以给它额外添加一些“技能”(增强逻辑)。
2 如何使用?
你需要添加 CGLIB 的依赖,如果你使用的是 Spring Boot,它通常会自动包含,如果需要手动添加,Maven 配置如下:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 使用较新版本 -->
</dependency>
使用步骤:
- 创建一个目标类(可以是一个普通类,不需要实现接口)。
- 创建一个
MethodInterceptor接口的实现类,这是拦截逻辑的核心。 - 使用
Enhancer类来配置和生成代理对象。
代码示例:
目标类
// RealObject.java - 没有实现任何接口
public class RealObject {
public void doSomething() {
System.out.println("RealObject: 正在执行核心业务逻辑...");
}
public String doSomethingElse(String input) {
System.out.println("RealObject: 正在执行另一个业务逻辑,输入是: " + input);
return "处理结果: " + input;
}
}
拦截器
// MyMethodInterceptor.java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyMethodInterceptor implements MethodInterceptor {
/**
* @param obj 代理对象(也就是生成的子类的实例)
* @param method 被调用的父类(目标类)的方法
* @param args 方法调用时传入的参数
* @param methodProxy 用于调用父类方法的代理对象
* @return 方法调用的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("【CGLIB 拦截器】方法调用前: " + method.getName());
// 调用父类的原始方法(即目标类的方法)
// 这里有两种方式:
// 1. method.invoke(target, args); // 如果有目标对象实例
// 2. methodProxy.invokeSuper(obj, args); // 推荐方式,性能更高
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("【CGLIB 拦截器】方法调用后: " + method.getName() + ", 返回值: " + result);
return result;
}
}
创建并使用代理
// CglibDemo.java
import net.sf.cglib.proxy.Enhancer;
public class CglibDemo {
public static void main(String[] args) {
// 1. 创建 Enhancer 对象,相当于 JDK 中的 Proxy
Enhancer enhancer = new Enhancer();
// 2. 设置父类(即要代理的类)
enhancer.setSuperclass(RealObject.class);
// 3. 设置回调对象,即我们的 MethodInterceptor 实现
enhancer.setCallback(new MyMethodInterceptor());
// 4. 创建代理对象
RealObject proxyObject = (RealObject) enhancer.create();
// 5. 通过代理对象调用方法
System.out.println("--- 调用 doSomething() ---");
proxyObject.doSomething();
System.out.println("\n--- 调用 doSomethingElse() ---");
String result = proxyObject.doSomethingElse("Hello CGLIB");
System.out.println("从主方法获取的最终结果: " + result);
}
}
输出结果:
--- 调用 doSomething() ---
【CGLIB 拦截器】方法调用前: doSomething
RealObject: 正在执行核心业务逻辑...
【CGLIB 拦截器】方法调用后: doSomething, 返回值: null
--- 调用 doSomethingElse() ---
【CGLIB 拦截器】方法调用前: doSomethingElse
RealObject: 正在执行另一个业务逻辑,输入是: Hello CGLIB
【CGLIB 拦截器】方法调用后: doSomethingElse, 返回值: 处理结果: Hello CGLIB
从主方法获取的最终结果: 处理结果: Hello CGLIB
3 与 JDK 动态代理的核心区别
| 特性 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现机制 | 基于接口,使用 java.lang.reflect.Proxy API |
基于类继承,使用 net.sf.cglib.proxy.Enhancer |
| 代理对象类型 | 实现目标类接口的类 | 目标类的子类 |
| 目标类要求 | 必须实现至少一个接口 | 可以没有接口,对普通类有效 |
| 性能 | 第一次调用较慢,后续调用与原生方法接近 | 整体性能通常优于 JDK 代理,invokeSuper 比 invoke 快 |
| 局限性 | 无法代理没有接口的类 | 无法代理 final 类和 final 方法 |
| 依赖 | JDK 内置,无需额外库 | 需要引入 cglib 第三方库 |
CGLIB 的高级特性
1 MethodInterceptor (核心拦截器)
这是最常用的回调类型,提供了对方法调用的完全控制,如上例所示,intercept 方法是核心,它可以在方法执行前后插入任意逻辑,并可以决定是否执行原始方法。
2 Enhancer (核心类)
Enhancer 是 CGLIB 中最核心的类,用于生成类,它的主要配置项包括:
setSuperclass(Class<?> superclass): 设置要生成的类的父类(即被代理的类)。setCallback(Callback callback): 设置一个单一的回调对象,作用于所有方法。setCallbacks(Callback[] callbacks): 设置一个回调数组,结合CallbackFilter使用,可以为不同方法指定不同的回调。setCallbackFilter(CallbackFilter filter): 设置回调过滤器,决定方法调用时使用哪个回调。create(): 执行生成操作并返回代理实例。
3 回调过滤器 (CallbackFilter)
当一个类中,我们想对不同方法采用不同的增强逻辑时,CallbackFilter 就派上用场了。
示例:
我们想对 doSomething() 方法进行日志增强,对 doSomethingElse() 方法进行性能监控增强。
// MyCallbackFilter.java
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import java.lang.reflect.Method;
public class MyCallbackFilter implements CallbackFilter {
// 定义两种回调
// NoOp.INSTANCE 表示不进行任何拦截,直接调用原方法
// new MyLoggingInterceptor() 是我们的日志拦截器
// new MyPerformanceInterceptor() 是我们的性能拦截器
@Override
public int accept(Method method) {
if (method.getName().equals("doSomething")) {
return 0; // 返回第一个回调
} else if (method.getName().equals("doSomethingElse")) {
return 1; // 返回第二个回调
}
// 默认不拦截
return 2;
}
}
// CglibFilterDemo.java
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
public class CglibFilterDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealObject.class);
// 创建回调数组
Callback[] callbacks = new Callback[]{
new MyLoggingInterceptor(), // 索引 0
new MyPerformanceInterceptor(), // 索引 1
NoOp.INSTANCE // 索引 2 (无操作)
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new MyCallbackFilter());
RealObject proxy = (RealObject) enhancer.create();
proxy.doSomething();
proxy.doSomethingElse("test");
}
}
CGLIB 的注意事项
1 性能
CGLIB 的性能通常优于 JDK 动态代理,尤其是在方法被多次调用时,这是因为 CGLIB 生成的字节码直接继承自目标类,而 JDK 代理需要通过反射 InvocationHandler 来分派调用,生成 CGLIB 代理类本身的开销比 JDK 代理要大。
2 构造器问题
CGLIB 通过生成子类来创建代理,因此它会调用目标类的无参构造器,如果目标类没有无参构造器,CGLIB 将会抛出异常。
public class NoArgConstructorRequired {
// 如果没有这个无参构造器,CGLIB会失败
public NoArgConstructorRequired() {
}
public void test() {
System.out.println("test");
}
}
3 final 方法/类
CGLIB 的核心是继承,根据 Java 的规则,final 类和 final 方法是不能被继承和重写的。
- 无法代理
final类:Enhancer.setSuperclass()会直接抛出异常。 - 无法代理
final方法:CGLIB 会忽略final方法的拦截,直接调用原始方法,增强逻辑不会生效。
如何选择?
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 目标类已经实现了接口 | 优先使用 JDK 动态代理 | JDK 是标准库,无额外依赖,更符合面向接口的设计思想。 |
| 目标类没有实现接口 | 必须使用 CGLIB | CGLIB 是唯一的选择(或者自己用字节码操作工具如 ASM 手动实现)。 |
需要代理 final 类或方法 |
CGLIB 不可行 | 需要考虑其他方案,如 AOP 框架(如 Spring)的组合代理或字节码增强。 |
| 对性能有极致要求 | CGLIB | CGLIB 的方法调用性能通常略优,但差别不大,对于绝大多数应用,JDK 代理已经足够快。 |
在现代框架(如 Spring)中,代理策略通常是自动选择的:
- 如果目标类实现了接口,默认使用 JDK 动态代理。
- 如果目标类没有实现接口,则自动降级使用 CGLIB。
- 你也可以通过配置强制 Spring 使用 CGLIB 代理(为了使用
@Transactional注解时能代理类本身的方法)。
希望这份详细的解释能帮助你完全理解 CGLIB 动态代理!
