深入解析代理模式:静态代理与动态代理的比较及JDK与CGLIB动态代理技术
1. 静态代理与动态代理的区别
静态代理和动态代理都是实现代理模式的方式,它们在实现上有很大的不同。下面是它们的主要区别:
实现方式不同
静态代理
静态代理是在编译期就已经确定代理对象的类型。代理类需要手动编写,并实现被代理类的接口。
动态代理
动态代理是在运行时动态生成代理对象,代理类不需要手动编写,而是由框架自动生成。Java中的动态代理通常使用Proxy
类和InvocationHandler
接口实现。
适用范围不同
静态代理
静态代理只适用于代理对象类型固定、接口较少的情况下。每增加一个被代理的接口,就需要编写一个新的代理类。
动态代理
动态代理可以代理任意的接口,无需编写新的代理类,因此更加灵活。适用于代理对象类型不固定、接口较多、灵活性要求较高的情况。
性能表现不同
静态代理
由于静态代理在编译期就已经确定代理对象的类型,因此在运行时执行效率较高。
动态代理
动态代理在运行时需要进行额外的代理对象生成、方法调用转发等操作,因此会存在一定的性能损失。
动态代理的类生成过程
Java虚拟机类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。
加载阶段
加载阶段需要完成以下三件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的
java.lang.Class
对象,作为方法区这个类的各种数据访问入口。
获取类的二进制字节流的方法
- 从本地获取:Java虚拟机可以从本地文件系统加载类的字节码文件。
- 从网络中获取:Java虚拟机也可以从网络中加载类的字节码数据。使用类加载器的
getResourceAsStream()
方法,通过指定URL的方式来获取InputStream
,然后通过读取InputStream
获取字节码数据。 - 运行时计算生成:这种场景使用最多的是动态代理技术,在
java.lang.reflect.Proxy
类中,就是用了ProxyGenerator.generateProxyClass
来为特定接口生成形式为*$Proxy
的代理类的二进制字节流。
总结
- 静态代理适用于代理对象类型固定、接口较少、性能要求较高的情况。
- 动态代理适用于代理对象类型不固定、接口较多、灵活性要求较高的情况。
通过动态代理,可以根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用,使得代理对象的生成和方法调用更加灵活和动态。
JDK动态代理和CGLIB动态代理是Java语言中实现动态代理的两种方式。它们之间的主要区别如下:
2. JDK动态代理与CGLIB动态代理的区别?
基于的技术不同
- JDK动态代理:基于Java的反射机制实现。使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来创建动态代理类。 - CGLIB动态代理:基于字节码生成技术实现。使用CGLIB库生成代理类,通过操作字节码来生成代理类的子类。
被代理类的要求不同
- JDK动态代理:只能代理实现了接口的类。代理类必须实现被代理类的所有接口。
- CGLIB动态代理:可以代理没有实现接口的类。代理类通过继承被代理类来生成代理对象。
代理性能不同
- JDK动态代理:生成的代理类性能相对较低,因为它是基于反射实现的,每次方法调用都需要通过反射机制来处理。
- CGLIB动态代理:生成的代理类性能相对较高,因为它是基于字节码生成技术实现的,直接生成字节码来处理方法调用。
代理方式不同
- JDK动态代理:基于接口实现的代理方式。代理类必须实现被代理类的所有接口。
- CGLIB动态代理:基于继承实现的代理方式。代理类通过继承被代理类来生成代理对象。
选择依据
- 如果被代理类实现了接口,优先选择JDK动态代理。
- 如果被代理类没有实现接口,只能使用CGLIB动态代理。
JDK动态代理示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("Real subject request.");
}
}
public class ProxyHandler implements InvocationHandler {
private Object realSubject;
public ProxyHandler(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result = method.invoke(realSubject, args);
postRequest();
return result;
}
private void preRequest() {
System.out.println("Pre request processing.");
}
private void postRequest() {
System.out.println("Post request processing.");
}
}
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
new ProxyHandler(realSubject)
);
proxy.request();
}
}
CGLIB动态代理示例
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 RealSubject {
public void request() {
System.out.println("Real subject request.");
}
}
public class ProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
preRequest();
Object result = proxy.invokeSuper(obj, args);
postRequest();
return result;
}
private void preRequest() {
System.out.println("Pre request processing.");
}
private void postRequest() {
System.out.println("Post request processing.");
}
}
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new ProxyInterceptor());
RealSubject proxy = (RealSubject) enhancer.create();
proxy.request();
}
}
底层实现
JDK动态代理
JDK生成代理对象依赖的是反射。动态代理类对象继承了Proxy
类,并且实现了被代理的所有接口。
CGLIB动态代理
CGLIB(Code Generation Library)是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。CGLIB基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)。Spring AOP(面向切面编程)基于CGLIB进行封装,实现CGLIB方式的动态代理。
总结
- JDK动态代理:适用于代理接口方法,性能稍低,使用反射机制。
- CGLIB动态代理:适用于代理普通类方法,性能较高,使用字节码生成技术。如果被代理类实现了接口,优先选择JDK动态代理;如果被代理类没有实现接口,那么只能使用CGLIB动态代理。
原文地址:https://blog.csdn.net/qq_26893655/article/details/140218977
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!