文章目录
前言
一、静态代理的弊端
二、动态代理的优势
三、动态代理使用流程
1、目标对象接口
2、被代理对象
3、调用处理程序
4、客户端
四、动态生成 代理对象 类 的 字节码 文件数据
前言
代理模式结构 : 代理模式中的元素有 客户端 , 主题对象 , 被代理对象 , 代理对象 ;
客户端 持有 主题对象 , 调用其方法 ;
代理对象 和 被代理对象 都是 主题 的子类 ;
代理对象 持有 被代理对象 , 可以调用 被代理对象 的方法 ;
代理模式的核心 : 代理对象 与 被代理对象 都实现同一个父类或接口 , 这样在客户端使用时 , 客户端 感觉自己与 被代理对象 沟通 , 但用户实际上与 代理对象 进行沟通 ;
一、静态代理的弊端
静态代理 中 , 代理对象 和 被代理对象 必须实现 主题对象 接口 , 如果 主题对象 接口发生改变 , 则相应的 代理对象 和 被代理对象 都要进行相应修改 ;
二、动态代理的优势
动态代理 解决了 静态代理的上述问题 , 不需要手动创建代理对象 , 由 Java 虚拟机实现 代理对象 , 该代理对象自动实现 主题对象 的接口 ;
动态代理执行时 , 动态地创建了字节码文件 , 生成了代理类 ;
三、动态代理使用流程
动态代理使用流程 :
① 创建目标对象 : 创建 目标对象 接口 ;
② 创建被代理对象 : 创建 被代理对象 , 实现 目标对象 接口 ;
③ 创建调用处理程序 : 创建 InvocationHandler 子类对象 , 内部持有 被代理对象 , 在 invoke 方法中 , 返回 method.invoke(subject, args) ;
④ 动态创建代理对象 : 调用 Proxy.newProxyInstance 创建 代理对象 实例对象 , 由 JVM 自动创建代理对象类 , 然后再创建对应的实例对象 ;
⑤ 动态代理调用 : 调用 代理对象 实例的相关 目标对象 接口 方法 ;
1、目标对象接口
/** * 目标接口 * 代理对象 和 被代理对象 都要实现该接口 */ public interface Subject { void request(); }
2、被代理对象
/** * 被代理对象 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("被代理对象 RealSubject request()"); } }
3、调用处理程序
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、客户端
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 字节码的规范 , 按照顺序依次写入 魔数 , 次版本号 , 主版本号 , 常量池 , 访问修饰符 , 类索引 等数据 ;