设计模式--代理模式(附源码分析)

1、简单的背景介绍

 在平时的开发过程中,我们实现方法的调用往往只是普通的对象调用方法,实现复杂的业务就是一层一层的对象调用方法依次进行实现,但是如果我要实现在某些方法执行前或者执行后都去执行某些特定的操作呢,这时候可以通过代理模式来实现。可以简单的理解为,以前你一个朋友是一个普通人 你可以随意的和他联系  但是现在这个朋友出名了  你现在和他联系都需要经过经纪人这个中间层  这个中间层可以对你的意图,信息进行各种各种操作后 再传到你的朋友那,这个中间层的起了至关重要的作用。【动态代理模式在mybatis的拦截器中使用可以使用该模式进行插件开发; 同时在Spring中的AOP原理也是采用该模式进行实现(两种代理模式)】

2、动态代理涉及的及各类以及接口

  1.被代理类(可理解为目标类 委托类 tagart  也就是你虽然要做代理 但是你总有最后调用一个实现方法吧 这个方法所属的类就是被代理类 ) 

  2.被代理类接口类  也就是被代理类需要实现一个interface 接口提取被代理类所有的方法

  3.invocationHandler类(这里称为中间类)实现invocationHandler接口 可以理解为中间类 该类中会调用被代理类的方法 在调用被代理类方法的前后 可以由程序猿们手动增加代码 添加自己相应的功能 这也是代理设计模式的最关键的地方

  4.代理类 也就是代理模式开始执行方法的类 该类继承了Proxy类 实现了被代理类的接口 为什么实现被代理类的接口 下面有源码分析。

3、根据上面的说明 来理解下面的话:

  代理模式是常用的java设计模式,他的特征是代理类与被代理类有同样的接口,代理类主要负责为被代理类预处理消息、过滤消息、把消息转发给被代理类,以及事后处理消息等。代理类与被代理类之间通常会存在关联关系,一个代理类的对象与一个被代理类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用被代理类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

4、动态代理的代码实现(通过代理理解上边的介绍)

   1.被代理类的接口

public interface Person {
    public void sayHello(String content, int age);
    public void sayGoodBye(boolean seeAgin, double time);
}

   2.被代理的类 需要实现其对应接口

public class Student implements Person{
 
    @Override
    public void sayHello(String content, int age) {
        // TODO Auto-generated method stub
        System.out.println("student say hello" + content + " "+ age);
    }
 
    @Override
    public void sayGoodBye(boolean seeAgin, double time) {
        // TODO Auto-generated method stub
        System.out.println("student sayGoodBye " + time + " "+ seeAgin);
    }
 
}

3.invocationHandler类 实现InvocationHandler接口 内部包含被代理类对象属性

/**
 * 动态代理,动态代理类不要显示的实现被代理类所实现的接口
 * 
 */
public class MyInvocationHandler implements InvocationHandler{
	
	private Object object;             //此处的Object类就是代表被代理类 因为通过该类会最终调用被代理类的方法
	
	public MyInvocationHandler(Object object){
		this.object = object;
	}
 
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
     {
	        //在这里 负责为被代理类预处理消息、过滤消息、把消息转发给被代理类,
		System.out.println("MyInvocationHandler invoke begin");
		System.out.println("proxy: "+ proxy.getClass().getName());
		System.out.println("method: "+ method.getName());
		for(Object o : args){
			System.out.println("arg: "+ o);
		}

	        //通过反射调用 被代理类的方法
		method.invoke(object, args);

          //被代理执行完毕后的事后处理消息
		System.out.println("MyInvocationHandler invoke end");
		return null;
	}

  4.代理类 通过Proxy类的NewProxyInstance创建:

Student s = new Student();
//这一句是生成代理类的class文件,前提是你需要在工程根目录下创建com/sun/proxy目录,不然会报找不到路径的io异常
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//获得加载被代理类的 类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//指明被代理类实现的接口
Class<?>[] interfaces = s.getClass().getInterfaces();
 // 创建被代理类的委托类,之后想要调用被代理类的方法时,都会委托给这个类的invoke(Object proxy, Method method, Object[] args)方法
MyInvocationHandler h = new MyInvocationHandler(s);
//生成代理类
Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h);
//通过代理类调用 被代理类的方法
proxy.sayHello("yujie.wang", 20);
proxy.sayGoodBye(true, 100);

  只要对代理类的内容进行说明 Person proxy = (Person)Proxy.newProxyInstance(loader, interfaces, h) 创建出 代理类  该代理类通过Proxy.instance()创建 参数1为类加载器 此处可以通过Thread.currentThread().getContextClassLoader()获得 也可以通过this.getClassLoader()获得 其实获得的是系统类加载器 AppClassLoader(); 参数2为接口类 也就是被代理类的实现接口;参数3为invocationHandler类。

  程序的执行顺序:
 proxy.sayHello("yujie.wang", 20)  ---->  h.invoke()  ---->  method.invoke(object, args) 因为此处传入的object是被代理类student 方法是sayHello 所有此处是调用 student。sayHello()方法。

 为什么执行代理对象的sayHello方法后 可以自动去调用中间invocationHandler类的invoke方法 看完下边的源码一目了然。

5、源码分析

   代理类编译后生成 $Proxy0.class 对生成的class进行反编译 如下:

//代理对象实现了Person类 
public final class $Proxy0 extends Proxy implements Person
{
  private static Method m4;
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;
  
  public $Proxy0(InvocationHandler paramInvocationHandler) throws 
  {
    super(paramInvocationHandler);
  }
   //创建代理的过程中 传入第二个参数interface的目的就在这里 实现了接口的方法
  public final void sayGoodBye(boolean paramBoolean, double paramDouble) throws 
  {
    try
    {
      // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
      // m4为静态代码块中通过反射获得的 Method
      this.h.invoke(this, m4, new Object[] { Boolean.valueOf(paramBoolean), Double.valueOf(paramDouble) });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final boolean equals(Object paramObject)
    throws 
  {
    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 int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //实现了Person接口的方法,这就是我们调用这个方法Proxy.newProxyInstance必须提供第二个参数的作用 
  public final void sayHello(String paramString, int paramInt)
    throws 
  {
    try
    {
      // 我们看到通过调用代理类的方法时,最终方法都会委托给InvocationHandler实现类的invoke方法
      // m4为代理类通过反射获得的Method
      this.h.invoke(this, m3, new Object[] { paramString, Integer.valueOf(paramInt) });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  //静态代码块 在代码加载的过程中实现
  static
  {
    try
    {//代理类通过反射 获得的接口方法Method
      m4 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayGoodBye", new Class[] { Boolean.TYPE, Double.TYPE });
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      //代理类通过反射 获得的接口方法Method
      m3 = Class.forName("com.yujie.proxy.dynamic.Person").getMethod("sayHello", new Class[] { Class.forName("java.lang.String"), Integer.TYPE });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逆向技术

C++反汇编第二讲,不同作用域下的构造和析构的识别

               C++反汇编第二讲,不同作用域下的构造和析构的识别 目录大纲:   1.全局(静态)对象的识别,(全局静态全局一样的,都是编译期间...

20610
来自专栏微信公众号:Java团长

Java动态代理原理及解析

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类...

984
来自专栏阿凯的Excel

Python读书笔记17(while与列表、字典)

今天分享利用while函数处理列表和字典,顺便温习一下历史知识 一、论如何将一个列表折腾至另外一个列表!(两个列表是独立的) 论折腾列表有几种方法! 先分...

3675
来自专栏java学习

Java基础总结大全(1)

一、基础知识: 1、JVM、JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性。 ...

42311
来自专栏我是攻城师

在Scala里面如何使用元组

3674
来自专栏Python爱好者

Python高效编程(三)

1385
来自专栏程序员同行者

python3模块: json & pickle

1222
来自专栏liulun

Nim教程【十五】【完结】

模版 模版是Nim语言中的抽象语法树,它是一种简单的替换机制,在编译期被处理 这个特性使Nim语言可以和C语言很好的运行在一起 像调用一个方法一样调用一个模版 ...

2348
来自专栏xx_Cc的学习总结专栏

C - 基础总结

36311
来自专栏java学习

Java基础总结大全(1)

一、基础知识: 1、JVM、JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性。 ...

3695

扫码关注云+社区

领取腾讯云代金券