文章目录
- 前言
- 一、静态代理的弊端
- 二、动态代理的优势
- 三、动态代理使用流程
- 1、目标对象接口
- 2、被代理对象
- 3、调用处理程序
- 4、客户端
- 四、动态生成 代理对象 类 的 字节码 文件数据
前言
代理模式结构 : 代理模式中的元素有 客户端 , 主题对象 , 被代理对象 , 代理对象 ;
- 客户端 持有 主题对象 , 调用其方法 ;
- 代理对象 和 被代理对象 都是 主题 的子类 ;
- 代理对象 持有 被代理对象 , 可以调用 被代理对象 的方法 ;
代理模式的核心 : 代理对象 与 被代理对象 都实现同一个父类或接口 , 这样在客户端使用时 , 客户端 感觉自己与 被代理对象 沟通 , 但用户实际上与 代理对象 进行沟通 ;
一、静态代理的弊端
静态代理 中 , 代理对象 和 被代理对象 必须实现 主题对象 接口 , 如果 主题对象 接口发生改变 , 则相应的 代理对象 和 被代理对象 都要进行相应修改 ;
二、动态代理的优势
动态代理 解决了 静态代理的上述问题 , 不需要手动创建代理对象 , 由 Java 虚拟机实现 代理对象 , 该代理对象自动实现 主题对象 的接口 ;
动态代理执行时 , 动态地创建了字节码文件 , 生成了代理类 ;
三、动态代理使用流程
动态代理使用流程 :
- ① 创建目标对象 : 创建 目标对象 接口 ;
- ② 创建被代理对象 : 创建 被代理对象 , 实现 目标对象 接口 ;
- ③ 创建调用处理程序 : 创建
InvocationHandler
子类对象 , 内部持有 被代理对象 , 在invoke
方法中 , 返回method.invoke(subject, args)
; - ④ 动态创建代理对象 : 调用
Proxy.newProxyInstance
创建 代理对象 实例对象 , 由 JVM 自动创建代理对象类 , 然后再创建对应的实例对象 ; - ⑤ 动态代理调用 : 调用 代理对象 实例的相关 目标对象 接口 方法 ;
1、目标对象接口
代码语言:javascript复制/**
* 目标接口
* 代理对象 和 被代理对象 都要实现该接口
*/
public interface Subject {
void request();
}
2、被代理对象
代码语言:javascript复制/**
* 被代理对象
*/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("被代理对象 RealSubject request()");
}
}
3、调用处理程序
代码语言:javascript复制import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicInvocationHandler implements InvocationHandler {
/**
* 持有的 被代理对象
*/
private Subject subject;
public DynamicInvocationHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用真实的 被代理对象 的方法
// 被代理对象的所有的方法的调用都会传到该方法中进行处理
Object object = method.invoke(subject, args);
return object;
}
}
4、客户端
代码语言:javascript复制import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// 被代理对象
Subject realSubject = new RealSubject();
// 创建调用处理程序 , 内部持有被代理对象
DynamicInvocationHandler dynamicInvocationHandler =
new DynamicInvocationHandler(realSubject);
// 生成动态代理类
Subject subject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(),
dynamicInvocationHandler);
// 动态代理调用
subject.request();
}
}
执行结果 :
四、动态生成 代理对象 类 的 字节码 文件数据
动态代理 中的 代理对象对应的 字节码类 是由 Java 虚拟机自动生成的 , 在 java.lang.reflect.Proxy
中 , 调用 ProxyGenerator.generateProxyClass
方法 , 生成了 代理对象 类 , 返回的 byte[]
数据就是字节码类对应的二进制数据 ;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
ProxyGenerator
的源码需要下载 JDK 源码查看 , 在 IntelliJ IDEA 开发环境中无法查看 ;
网上找到了一篇博客 , 对此描述的很清楚 JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析 ;
ProxyGenerator
中的 generateProxyClass
方法中 , 主要调用了 generateClassFile
方法 , 按照 Class 字节码的规范 , 按照顺序依次写入 魔数 , 次版本号 , 主版本号 , 常量池 , 访问修饰符 , 类索引 等数据 ;