JDK 动态代理和 Cglib 代理的区别?
文章目录
- [JDK 动态代理和 Cglib 代理的区别?](#JDK 动态代理和 Cglib 代理的区别?)
JDK 动态代理和 CGLIB 代理是 Java 中实现代理机制的两种常用技术,各自有不同的实现机制和适用场景。以下是它们的主要区别:
1. 代理机制
-
JDK 动态代理:
- 基于接口:JDK 动态代理只能对实现了接口的类创建代理。它通过
java.lang.reflect.Proxy
类来生成代理对象。 - 实现方式:代理对象会实现一个或多个接口,并将所有方法调用转发到
java.lang.reflect.InvocationHandler
接口的实现类中。 - 创建方式:使用
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法创建代理对象。
- 基于接口:JDK 动态代理只能对实现了接口的类创建代理。它通过
-
CGLIB 代理:
- 基于类:CGLIB 代理可以代理没有实现接口的具体类。
它通过继承目标类来生成代理类
。 - 实现方式:生成目标类的子类,并在子类中重写目标方法,使用
org.springframework.cglib.proxy.Enhancer
类来创建代理对象。 - 创建方式:使用
Enhancer.setSuperclass(Class superclass)
方法设置目标类,并通过Enhancer.setCallback(MethodInterceptor callback)
方法设置方法拦截器。
- 基于类:CGLIB 代理可以代理没有实现接口的具体类。
2. 适用场景
-
JDK 动态代理:
- 适用接口的情况:适用于目标类实现了一个或多个接口的情况。
- 轻量级:由于只生成实现了接口的代理对象,生成的代理类相对较轻量。
-
CGLIB 代理:
- 适用具体类的情况:适用于目标类没有实现接口的情况,或者你需要代理具体类的情况。
- 支持 final 类和方法:
CGLIB 代理可以处理没有实现接口的类
,但不能代理 final 类和 final 方法(因为 final 方法不能被重写)
。
3. 生成代理的方式
-
JDK 动态代理:
- 生成方式:在运行时生成一个实现了接口的代理类。代理类的字节码由 Java 虚拟机(JVM)动态生成。
- 方法转发:所有方法调用都会转发到实现了
InvocationHandler
接口的对象中。
-
CGLIB 代理:
- 生成方式:在运行时生成目标类的子类。子类重写目标类的方法,并通过
MethodInterceptor
处理方法调用。 - 字节码操作:使用字节码操作库生成目标类的子类,可能涉及到复杂的字节码操作和类生成过程。
- 生成方式:在运行时生成目标类的子类。子类重写目标类的方法,并通过
4. 性能开销
-
JDK 动态代理:
- 性能开销:相对较小,因为只需要处理接口方法的调用。方法调用的实际逻辑在
InvocationHandler
中处理。
- 性能开销:相对较小,因为只需要处理接口方法的调用。方法调用的实际逻辑在
-
CGLIB 代理:
- 性能开销:相对较大,因为需要生成目标类的子类,涉及到更多的字节码生成和继承操作。生成的代理类可能更大,内存占用更高。
5. 兼容性
-
JDK 动态代理:
- 兼容性:不能代理 final 类和 final 方法。如果目标类或方法是 final,则无法使用 JDK 动态代理。
-
CGLIB 代理:
- 兼容性:不能代理 final 类和 final 方法。如果目标类是 final,则 CGLIB 也不能代理。如果目标方法是 final,CGLIB 无法拦截。
示例代码
JDK 动态代理
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkDynamicProxyDemo {
public interface Hello {
void sayHello();
}
public static class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("你好,世界!");
}
}
public static class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前");
Object result = method.invoke(target, args);
System.out.println("方法调用后");
return result;
}
}
public static void main(String[] args) {
Hello hello = new HelloImpl();
Hello proxy = (Hello) Proxy.newProxyInstance(
hello.getClass().getClassLoader(),
hello.getClass().getInterfaces(),
new MyInvocationHandler(hello)
);
proxy.sayHello();
}
}
CGLIB 代理
java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyDemo {
public static class Hello {
public void sayHello() {
System.out.println("你好,世界!");
}
}
public static class MyMethodInterceptor implements MethodInterceptor {
private final Object target;
public MyMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("方法调用前");
Object result = proxy.invoke(target, args);
System.out.println("方法调用后");
return result;
}
}
public static void main(String[] args) {
Hello hello = new Hello();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new MyMethodInterceptor(hello));
Hello proxy = (Hello) enhancer.create();
proxy.sayHello();
}
}
总结
- JDK 动态代理:轻量、简单,适用于接口,不能代理 final 类和方法。
- CGLIB 代理:功能强大,适用于具体类,不能代理 final 类和方法,可能会有较大的性能开销。