Java动态代理的理解

主要写一下动态代理模式

采用的InvocationHandler的方式,采用CgLib方式的看看后面能不能写出来。


动态代理模式的优点

  1. 在不知道代理类的情况下便可以创建类并且调用方法。

       /**
       这里几个参数的含义
       proxy 是真正的生成的被代理的对象
       proxyT是这个真正的代理类的对象
       这里便可以看出动态代理的优点:在不知道被代理的对象是什么以及要实现的接口是什么样的情况下,我们便可以构造出了一个这样动态代理类。
       在每个方法前面输出 ”冀海川真帅”。
       */
    		public class proxyGenerate<T> {
       				T proxy;
       				public T bind(T proxyB){
       					this.proxy = proxyB;
       					return  Proxy.newInstance(proxy.getClass().getClassLoader(),proxy.getClass().getInterfaces(),methodInvocationHadler);
       				}
       				public Object methodInvocationHandler implements implements InvocationHandler thorws Exception{
       						@Override 
       						public Object invoke(Object proxyT,Method method,Object[] args){
       							System.out.println("冀海川真的帅");
       							return method.invoke(proxy,args);
       						}
       				}
       }
       
    

对上面的方法进行解释:

首先是 InvocationHandler,下面是它的源码:

    public interface InvocationHandler {
		    public Object invoke(Object proxy, Method method, Object[] args)
		        throws Throwable;
	}

这是一个接口,但是这个接口只有一个方法,这个接口主要的作用是利用代理类去调用被代理的对象的方法。
通过的是

return method.invoke(proxy,args);

这一样代码来实现的,同理我们也可以在上面添加我们自己定义的方法,这很像Spring当中的AOP编程中的切点与切面的问题。

下面是对 Proxy.newInstance(getClass().getClassLoader(),getClass().getInterfaces(),InvocationHandler);方法的解释

这个方法是用来创建被代理的对象。解释一下三个参数的含义

  1. 是为了得到被代理的对象的类加载器,类加载器保证了我们加载的类是同一个类(改天我会写一篇关于JVM类加载的机制)
  2. 这个参数值得注意,是得到被代理类所有实现的接口,这一点特别重要(因为动态代理模式有两种形式,一种是利用这篇文章的动态代理,还有一种是利用CgLib的方式进行动态代理),两种方式的区别就在于前者只能对实现了统一接口的类动态代理。
  3. 第三个参数 我理解为利用反射的方式去调用被代理类的方法。

我们可以通过两步来看到生成的代理类的源码

public class Test {
	public static void main(String[] args){
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//1
		Person p = new Person();
		Hello test = new proxyGenerate<Hello>().bind(p);
	    System.out.println(test.getClass());

		
		test.sayHello();
	}
}

标1的地方是
第一步:这一步的目的是为了通过Java虚拟机写入一个属性,让虚拟机中保存生成的代理类。这里要注意的是,要在自己的目录下创建 com/sun/proxy/$Proxy.class这个文件
在这里插入图片描述
具体的位置就像面的图一样。

第二步:就是对刚才生成的类进行反编译,这里我是用的jd-gui-1.4.0,我不知道为什么在DOS命令里面生成的javap -c 这个生成的命令是JAVA虚拟机的字节码。虽然不是二进制了但是字节码也是看起来有点吃力。反编译以后的源码为


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;


public final class $Proxy0
  extends Proxy
  implements Hello
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject)
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  -------------------------------------------------------------
 public final void sayHello()
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  --------------------------------------------------------------------
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
        --------------------------------------------------------------------
      m3 = Class.forName("����JVM��������������������������.Hello").getMethod("sayHello", 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]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

这里我截取的代码并不是全部 还有toString()等方法,因为这些方法在所有类中都存在就不介绍了
大概解释全部的代码
首先是这个Proxy0这个类继承了Proxy 并且在构造函数中传递了一个InvocationHandler这样的一个参数:

这就意味着调用被代理类的方法是由Proxy这个类实现的。

用 --------------------------------------------------------------------标出来的代码需要注意:

this.h.invoke(this, m3, null);

这个就很明显的解释了h是继承自Proxy类的InvocationHandler ,直接调用InvocationHandler的invoke的方法,对应于理类的sayHello()。m3对应的方法是

    --------------------------------------------------------------------
  m3 = Class.forName("����JVM��������������������������.Hello").getMethod("sayHello", new Class[0]);
    --------------------------------------------------------------------

就是借口当中的sayHello方法。

最麻烦的部分写完了下面的就是测试了。
首先写一个接口 Hello

public interface Hello {
	public void sayHello();
}

然后定义一个被代理类Person

public class Person implements Hello{
	public void sayHello(){
		System.out.println("Hello,jhc");
	}
}

最后是一个测试类前面也写了再写一遍

public class Test {
	public static void main(String[] args){
		System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		Person p = new Person();
		Hello test = new proxyGenerate<Hello>().bind(p);
	    System.out.println(test.getClass());

		
		test.sayHello();
	}
}

最后形成的目录结构是这样的:

在这里插入图片描述

最后输出的结果为:
在这里插入图片描述

主要参考的是《深入理解JAVA虚拟机》的第九章

猜你喜欢

转载自blog.csdn.net/weixin_43872339/article/details/84886467
今日推荐