【设计模式】代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )

2023-03-29 17:25:33 浏览数 (1)

文章目录

  • 前言
  • 一、静态代理的弊端
  • 二、动态代理的优势
  • 三、动态代理使用流程
    • 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[] 数据就是字节码类对应的二进制数据 ;

代码语言:javascript复制
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

ProxyGenerator 的源码需要下载 JDK 源码查看 , 在 IntelliJ IDEA 开发环境中无法查看 ;

网上找到了一篇博客 , 对此描述的很清楚 JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析 ;

ProxyGenerator 中的 generateProxyClass 方法中 , 主要调用了 generateClassFile 方法 , 按照 Class 字节码的规范 , 按照顺序依次写入 魔数 , 次版本号 , 主版本号 , 常量池 , 访问修饰符 , 类索引 等数据 ;

0 人点赞