当前位置: 首页 > 图灵资讯 > 技术篇> java代理模式的实现

java代理模式的实现

来源:图灵教育
时间:2023-05-21 09:25:27

1.代理模式的本质:

代理模式是在访问实际对象时引入一定程度的间接性,因为这种间接性可以添加多种用途。

2.代理模式的用途:

代理与委托有相同的界面。代理主要负责委托预处理信息、过滤信息、将信息转发给委托和事后处理信息。

3.代理模式的特点

代理类和委托类之间通常有关联,一个代理类对象与一个委托类对象有关,代理对象本身并没有真正实现服务,而是通过调用委托对象的相关方法来提供特定的服务

4.静态代理

程序员创建或自动生成特定工具的源代码界面、代理、代理等已在编译过程中确定。在程序运行之前,代理.class文件已经生成

5.动态代理

程序运行中创建的代理模式。动态代理的优点是,在不修改每个代理类别的方法的情况下,可以方便地统一处理代理函数。

6.动态代理的实现方式

JDK动态代理:

基于java的核心组件:.lang.Proxy类和InvocationHandler接口在reflect包下提供,生成动态代理类和动态代理对象。

核心实现过程:

1.代理类的生成

通过Proxy创建代理对象

主要有三个参数:代理加载器、代理接口数组、代理相关InvocationHandler实现对象。请注意,这里必须是一个对象。

Proxy.newProxyInstance(代理加载器, 代理类实现接口,代理类InvocationHandler实现类对象 );

2.实现代理方法

创建InvocationHandler实现类,持有代理对象的实例target。InvocationHandler中有一种invoke方法,所有执行代理对象的方法都将被执行invoke方法所取代。在invoke方法中执行代理对象target的相应方法。在代理过程中,我们在真正执行代理对象的方法之前加入了自己的其他处理。

public Object invoke(Object proxy, Method method, Object[] args)

3.代理产生的原理分析

使用Proxynewproxyinstance方法创建了一个动态代理对象,查看该方法的源代码,发现它只包装了创建动态代理的步骤

final Class<?>[] intfs = interfaces.clone();Class<?> cl = getproxyclass(loader, intfs);final Constructor<?> cons = cl.getConstructor(constructorParams);return cons.newInstance(new Object[]{h});

最重要的是要注意 Class<?> cl = getproxyclass(loader, intfs);这句话在这里生成了代理类,代码后面的构造器也是通过这里生成的类获得的。可以看出,这类文件的生成是整个动态代理的关键。因为是动态生成的文件,只需要知道这类文件缓存在java虚拟机中。我们可以通过以下方法将其打印到文件中,看看真相:

byte[] classFile = ProxyGenerator.generateProxyClass($Proxy0”,Student.class.getInterfaces());String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";try(FileOutputStream fos = new FileOutputStream(path)) {    fos.write(classFile);    fos.flush();    System.out.println(成功写入代理class文件);} catch (Exception e) {    System.out.println(“文件错误”);}

反编译这个class文件

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;import proxy.Person; public final class $Proxy0 extends Proxy implements Person {  private static Method m1;  private static Method m2;  private static Method m3;  private static Method m0;    /**  *请注意,这里是生成代理类的结构方法,方法参数为InvocationHandler类型。看到这个,你有点清楚吗?  *为什么代理对象的调用方法是执行InvocationHandler中的invoke方法,而InvocationHandler持有另一个?  *被代理对象的例子不禁想,是不是?..? 是的,就是你想的。 是的,就是你想的。  *  *super(paramInvocationHandler),调用父类Proxy的结构方法。  *父类持有:protected InvocationHandler h;  *Proxy结构方法:  *    protected Proxy(InvocationHandler h) {  *         Objects.requireNonNull(h);  *         this.h = h;  *     }  *  */  public $Proxy0(InvocationHandler paramInvocationHandler)    throws   {    super(paramInvocationHandler);  }    //这个静态块最初是在最后,我把它拿到前面,方便描述   static  {    try    {      ///看看这里的静态块里有什么,是否找到了givemoney的方法。请记住givemoney通过反射获得的名字m3,其他人不在乎      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);      m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      return;    }    catch (NoSuchMethodException localNoSuchMethodException)    {      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    }    catch (ClassNotFoundException localClassNotFoundException)    {      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    }  }   /**  *   *这里调用代理对象的giveMoney方法,InvocationHandler中的invoke方法直接调用,m3传输。  *this.h.invoke(this, m3, null);这里简单明了。  *this.h.invoke(this, m3, null);这里简单明了。  *再想想,代理对象持有InvocationHandler对象,InvocationHandler对象持有代理对象,  *再联系InvacationHandler中的invoke方法。嗯,就是这样。  */  public final void giveMoney()    throws   {    try    {      this.h.invoke(this, m3, null);      return;    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }  //注意,为了节省空间,这里省去了tostring,hashCode、equals方法的内容。原理和giveMoney方法一样。原理和giveMoney方法一样。}

通过对上述源码的分析,得出的关键结论如下:

proxy代理的核心结构是中介InvocationHandle的结构,以及当方法被调用时映射到InvocationHandler的invoke方法

Invocationhandler被视为中介类,中介类持有被代理对象,并在invoke方法中调用被代理对象的相应方法。通过聚合持有被代理对象的引用,最终将外部对invoke的调用转化为被代理对象的调用。

代理调用自己的方法时,通过自己持有的中介对象调用中介对象的invoke方法,从而实现代理执行被代理对象的方法。也就是说,动态代理通过中介实现了具体的代理功能。

7.JDK动态代理和CGlib的特点

总结JDK和CGLIB动态代理

JDK动态代理只能生成实现接口的代理,而不是针对类别 ,使用的是 实现Java反射技术,生成过程相对高效。

CGLIB是针对类实现代理,主要是为指定类生成子类,覆盖其中的方法 ,实现asm字节码框架,相关执行过程更有效,生成过程可以通过缓存来弥补,因为它是继承,所以最好不要声明这种或方法是final

JDK代理不需要第三方库的支持,只需要JDK环境就可以代理。使用条件:实现InvocationHandler + 使用Proxy.newproxyinstance生成代理对象 + 代理对象必须实现接口

CGLib必须依靠CGLib的类库,但它需要类来实现任何接口代理。指定类生成子类,覆盖其中的方法是在接口编程环境下继承但推荐JDK的代理;