从静态代理到动态代理

简介: 代理模式代理模式是非常常见的设计模式,在功能增强方面使用的特别明显,例如数据库连接池会使用代理连接代理真实的物理连接,以达到close只是归还到池中而不是真实关闭的效果。

代理模式

代理模式是非常常见的设计模式,在功能增强方面使用的特别明显,例如数据库连接池会使用代理连接代理真实的物理连接,以达到close只是归还到池中而不是真实关闭的效果。

模式介绍

代理模式可以说是特别容易上手的一个模式,因为现实生活中就有很多的代理,理解起来相对是比较容易的。


图片描述
图片描述
  1. 代理类实现了和实现类一样的接口
  2. 代理类依赖实现类
  3. 调用其实使用的是代理对象

模式优点和缺点

优点

当对现有功能增强的时候不需要修改已经实现的部分,只需要写代理类即可,满足了开闭原则。

缺点

当接口发生变化的时候,不但实现类要改动,代理类也要跟着改动。例如新增和修改了接口,代理类作为接口的实现类,也需要实现这些。

java的动态代理

动态代理的出现解决了模式的缺点

动态代理的简易展示

class TestInvocationHanlder implements InvocationHandler{
    ITest target;
    public TestInvocationHanlder(ITest target) {
        super();
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        method.invoke(target, args);
        System.out.println("after");
        return null;
    }
    
    public static void main(String[] args) {
        
        TestInvocationHanlder hanlder =new TestInvocationHanlder(new TestImpl());
        ITest newProxyInstance = (ITest) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{ITest.class} , hanlder);
        newProxyInstance.hello();
    }

上面代码锁展示的部分就是一个动态代理的操作过程。
主要就是通过newProxyInstance产生代理对象。

InvocationHandler的作用

看了代码的人一个最大的疑问就是为什么调用的代码最后会走入到InvocationHandler的实现中去。
我们先来看看newProxyInstance是做了什么操作

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        final Class<?>[] intfs = interfaces.clone();
        Class<?> cl = getProxyClass0(loader, intfs);
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return cons.newInstance(new Object[]{h});

    }

以上代码是去除了检验操作的部分,我们可以清晰的看到,这里是构造了一个cl的对象,并且把InvocationHandler 作为构造的参数传入的。

getProxyClass0是一个创造字节码并且类加载的过程。 我们可以通过ProxyGenerator看到动态字节码生成后的结果。

        byte[] proxyClassFile = ProxyGenerator.generateProxyClass("Proxy", new Class[] { ITest.class });
        Files.copy(new ByteArrayInputStream(proxyClassFile), Paths.get("F:\\code\\Proxy.class"));

使用如上代码,就可以生成一个名叫proxy的字节码文件

public final class Proxy extends java.lang.reflect.Proxy implements ITest
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public Proxy(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  }
  
  public final void hello()
  {
      this.h.invoke(this, m3, null);
      return;
  }
  
  public final String toString()
  {
      return (String)this.h.invoke(this, m2, null);
  }
  
  public final int hashCode()
  {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  
  static
  {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("proxy.ITest").getMethod("hello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

  }
}

以上也是省去了部分代码的结果,我们看到proyx是一个继承了reflect并且实现了我们的接口的类,他的构造参数就是InvocationHandler ,我们可以看到里面的方法,都是直接用InvocationHandler 的invoke来调用的,具体的方法,上面的声明的method就是具体方法调用时,就是调用invoke传入的参数。

动态代理流程

java直接生成了一个实现接口的字节码,对应我们的代理类,然后具体的流程必须我们编写invocationhandler来调用,代理类就是调用invocationhandler,以此达到动态的效果。


图片描述
图片描述

真实运行起来的效果就是上面的类图,只不过这个proxy是动态生成代理类的过程,由于代理类是根据接口动态生成的,所以在接口变化的时候,只要是逻辑没变的情况下,动态代理是不需要修改代码的。

总结

动态代理的本质就是动态生成代理类,他解决了接口变化带来的代码变化,例如接口新增了方法a,那么不用修改代码,代理就可以做到对a的增强。但是他无法解决对部分方法做增强的问题,例如原来的方法a,b,c,是对a是用a逻辑增强,对b有b逻辑增强。这块逻辑都要写在invocationhanlder的invok方法里,当遇到这种情况,修改invocationhandler是必须的了。

目录
相关文章
|
机器学习/深度学习 异构计算 Python
Bert-vits2最终版Bert-vits2-2.3云端训练和推理(Colab免费GPU算力平台)
对于深度学习初学者来说,JupyterNoteBook的脚本运行形式显然更加友好,依托Python语言的跨平台特性,JupyterNoteBook既可以在本地线下环境运行,也可以在线上服务器上运行。GoogleColab作为免费GPU算力平台的执牛耳者,更是让JupyterNoteBook的脚本运行形式如虎添翼。 本次我们利用Bert-vits2的最终版Bert-vits2-v2.3和JupyterNoteBook的脚本来复刻生化危机6的人气角色艾达王(ada wong)。
Bert-vits2最终版Bert-vits2-2.3云端训练和推理(Colab免费GPU算力平台)
|
Cloud Native 持续交付 测试技术
ALPD——驱动业务创新的精益产品开发
ALPD——驱动业务创新的精益产品开发
7145 0
ALPD——驱动业务创新的精益产品开发
|
Java Apache
Apache PDFbox快速开发指南
一、介绍 Apache PDFbox是一个开源的、基于Java的、支持PDF文档生成的工具库,它可以用于创建新的PDF文档,修改现有的PDF文档,还可以从PDF文档中提取所需的内容。
2448 0
|
2月前
|
机器学习/深度学习 人工智能 芯片
42_大语言模型的计算需求:从GPU到TPU
随着2025年大语言模型技术的持续突破和规模化应用,计算资源已成为推动AI发展的关键驱动力。从最初的CPU计算,到GPU加速,再到专用AI加速器的崛起,大语言模型的计算需求正在重塑全球数据中心的基础设施架构。当前,全球AI半导体市场规模预计在2027年将达到2380亿美元(基本情境)甚至4050亿美元(乐观情境),这一增长背后,是大语言模型对计算能力、内存带宽和能效比的极致追求。
|
监控 安全 网络安全
网络安全中的网络隔离与访问控制技术
【7月更文挑战第5天】网络隔离与访问控制技术是网络安全领域的重要组成部分。通过合理的网络隔离和严格的访问控制策略,可以有效保护网络资产免受未经授权的访问和攻击。随着技术的不断发展,我们需要不断探索和创新,以应对日益复杂的网络安全威胁。
|
数据安全/隐私保护 数据格式
高效的数据脱敏策略
在数字化时代,数据安全和隐私保护变得尤为重要。数据脱敏作为一种有效的数据保护手段,可以帮助企业降低数据泄露风险,同时遵守相关的法律法规。本文将介绍三种常见的数据脱敏方案,为您提供实用的技术干货。
373 1
|
IDE Java 开发工具
Java开发环境配置“IntelliJ IDEA”,超详细整理,适合新手入门
Java开发环境配置“IntelliJ IDEA”,超详细整理,适合新手入门
665 0
Java开发环境配置“IntelliJ IDEA”,超详细整理,适合新手入门
|
网络协议 数据库 网络架构
ospf多区域原理和配置
ospf多区域原理和配置
558 0
ospf多区域原理和配置
|
IDE Linux 开发工具
CentOS系统 NTFS-3G挂载NTFS分区
CentOS系统 NTFS-3G挂载NTFS分区
1167 0