引言
讲解JDK动态代理以及CGLib动态代理。
1. JDK动态代理
1.1 创建代理类及返回代理对象
jdk动态代理通过 proxy 类的静态方法 newProxyInstance 来创建代理类 并且 返回代理类 对象
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
其中
loader:类加载器,指定类加载器是为了精确地定位类。
interfaces:接口的Class类数组。使用JDK的反射实现动态代理必须要有接口,因为生成的代理需要实现这些接口,这样生成的代理类对象才能转化为代理目标的接口对象,进而根据接口中的方法调用处理器中的invoke方法。
h:InvocationHandler类型,它是每个代理类对应的处理器
1.2 InvocationHandler
InvocationHandler是每个代理类对应的处理器,它定义了一个invoke方法,其签名如下:
Object invoke(Object proxy, Method method, Object[] args)
其中
Object:方法调用的返回值,该返回值可以作为被代理的方法调用的返回值。
proxy:代理类对象。
method:目标类中被代理的方法。
args:目标类中被代理的方法的运行参数。
1.3 JDK动态代理的不足
在JDK中使用动态代理,必须有类的接口。因为生成的代理需要实现这个接口,这样我们生成的代理类对象才能转化为代理目标的接口对象,然后根据接口中的方法,调用处理器中invoke
方法
2. Cglib动态代理
2.1 概述
为了弥补JDK动态代理的不足,第三方组织封装了一套工具包,即Cglib的工具包。这套包不基于接口,而是基于父子继承,通过重写的形式扩展方法,并且这个子类工具是自动生成的。
CGLIB(Code Generation Library)是一个强大的字节码生成库,常用于在运行时动态生成某个类的子类,实现方法拦截等功能。与JDK动态代理不同,CGLIB是通过继承目标类来创建代理对象的,因此它不要求目标类实现接口,适用于没有实现接口的类。
CGLIB动态代理的基本原理是:通过字节码技术,在运行时生成目标类的子类,并重写其中的方法(即拦截方法的调用)。
目标类不需要实现接口:CGLIB可以对没有实现接口的类进行代理,而JDK动态代理只能代理实现了接口的类。
通过继承的方式创建代理类:CGLIB通过继承目标类并重写其中的方法来实现动态代理,因此代理类是目标类的子类。
性能比JDK动态代理高:虽然JDK代理适用于接口,但CGLIB通过字节码修改直接在目标类基础上生成代理对象,相对更高效。
2.2 CGLIB代理的使用
CGLIB代理是通过继承目标类并在其方法上添加拦截逻辑来实现的。CGLIB的核心类是 Enhancer,它用于创建代理对象。
步骤
添加CGLIB依赖:在项目中需要添加CGLIB的依赖,如果使用Maven管理项目,可以在pom.xml中添加如下依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
创建目标类:CGLIB代理是通过继承目标类来实现的,因此目标类不需要实现接口。
public class Hello {
public void sayHello(String name) {
System.out.println("Hello,World " + name);
}
}
创建 MethodInterceptor 实现类:MethodInterceptor 接口用于拦截目标类的方法调用。
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class HelloInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call: " + method.getName());
Object result = proxy.invokeSuper(obj, args); // 调用父类方法(目标类方法)
System.out.println("After method call: " + method.getName());
return result;
}
}
创建代理对象:使用 Enhancer 创建目标类的代理对象。
import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
// 创建目标对象
Hello hello = new Hello();
// 创建CGLIB代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class); // 设置目标类
enhancer.setCallback(new HelloInterceptor()); // 设置拦截器
// 创建代理对象
Hello proxy = (Hello) enhancer.create();
// 调用代理对象的方法
proxy.sayHello("World");
}
}
输出结果
Before method call: sayHello
Hello, World
After method call: sayHello
2.3 关键类和方法
Enhancer:CGLIB的核心类,通过它可以生成代理对象。
setSuperclass(Class):设置代理类的父类。
setCallback(MethodInterceptor):设置拦截器,它会在调用目标方法时拦截并执行自定义逻辑。
create():创建代理对象。
MethodInterceptor:这是CGLIB的拦截器接口,intercept()方法会在代理对象的方法被调用时执行。在 intercept() 中,通常会调用 MethodProxy.invokeSuper() 来调用目标类的实际方法。
MethodProxy:CGLIB提供的工具类,invokeSuper() 方法用于执行父类(目标类)的方法。
2.4 与JDK动态代理的对比
代理方式不同:JDK动态代理是基于接口的,而CGLIB基于类继承。
目标类要求:JDK动态代理要求目标类实现接口,而CGLIB不需要目标类实现接口,可以代理没有接口的类。
性能:CGLIB由于使用继承的方式创建代理对象,相较于JDK动态代理可能稍微高效一些,但由于是通过字节码生成,所以性能开销也不能忽视。
限制:CGLIB不能代理 final 类和 final 方法,因为CGLIB是通过继承和覆盖父类的方法来实现的,final 修饰符会阻止方法的重写。
2.5 应用场景
没有接口的类:当目标类没有实现接口时,JDK动态代理无法使用,这时CGLIB是一个好的选择。
性能要求较高:CGLIB代理创建代理类的方式比JDK代理更直接,通常性能较好,适合对性能有要求的场景。
3. 总结
JDK 动态代理和 CGLIB 动态代理的底层实现都与字节码相关,但具体实现方式不同:
3.1 JDK 动态代理的底层实现
JDK 动态代理的核心是 基于接口的反射机制,依赖 java.lang.reflect.Proxy
和 InvocationHandler
实现,其底层会 在运行时动态生成字节码,但不需要直接操作字节码库(如 ASM)。具体流程如下:
动态生成代理类:
当调用Proxy.newProxyInstance()
时,JVM 会在内存中动态生成一个代理类(如$Proxy0
),该类会实现目标接口。方法调用转发:
代理类中的方法会通过InvocationHandler.invoke()
将调用转发到目标对象。字节码生成方式:
JDK 动态代理的字节码生成由 JVM 内部实现(通过sun.misc.ProxyGenerator
类生成字节码),开发者无需直接操作字节码。
3.2 CGLIB 动态代理的底层实现
CGLIB 的底层基于 字节码操作库(如 ASM),直接生成目标类的子类,并通过继承重写方法的方式实现代理。具体流程如下:
动态生成子类:
CGLIB 使用 ASM 库直接操作字节码,生成目标类的子类(如TargetClass$$EnhancerByCGLIB$$...
)。方法拦截:
子类重写父类方法,并通过MethodInterceptor
拦截方法调用。字节码生成方式:
CGLIB 显式使用 ASM 库 生成和修改字节码,需要依赖cglib
和asm
的 JAR 包。
3.3 对比总结
3.4 示例代码
JDK 动态代理生成字节码
// 目标接口
public interface UserService {
void save();
}
// 代理生成代码
UserService proxy = (UserService) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{UserService.class},
(InvocationHandler) (p, method, args) -> {
System.out.println("Before method");
return method.invoke(target, args);
}
);
// 生成的代理类字节码(示例)
public final class $Proxy0 extends Proxy implements UserService {
public final void save() {
super.h.invoke(this, m3, null);
}
}
CGLIB 动态代理生成字节码
// 目标类(无需实现接口)
public class UserService {
public void save() {
System.out.println("Save user");
}
}
// 使用 CGLIB 生成代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Before method");
return proxy.invokeSuper(obj, args);
});
UserService proxy = (UserService) enhancer.create();
// 生成的子类字节码(示例)
public class UserService$$EnhancerByCGLIB$$1234 extends UserService {
public void save() {
MethodInterceptor interceptor = ...;
interceptor.intercept(this, method, args, proxy);
}
}