代理模式(Proxy)

代理模式简介    

    代理模式通常用于达到对原有系统功能进行扩充的目的。比如,原有系统中某个接口的实现类,仅仅只有输出的功能,缺少部分日志打印的功能,我们又不想去动原来的代码,这样的话代理模式就可以很好的帮我们解决这个问题。

    代理模式分为两种,静态代理和动态代理,其中代理模式存在三要素,分别是接口类、真实对象、代理对象。

静态代理

    下面我直接以代码对这个功能进行讲解。

    //定义接口

    代理模式(Proxy)

    //真实类

    代理模式(Proxy)

      //代理类

    代理模式(Proxy)

    静态代理是最为简单也最容易实现的一种代理模式,但是在大多数情况下,我们的系统远远要比样例要复杂的多,它可能同时有多个功能接口IActionA、IActionB、IActionC、IActionD......等等

    如果这个时候,我们依然使用静态代理这种方式来实现应用功能的扩展,对于这种情况有两种解决方案:对应每个接口来实现对应的代理类或者在同一个代理类里面同时实现所有的接口。我们分析一下上面这两种方式的缺点,第一种方式,很明显随着系统功能扩展接口增加,我们需要维护的代理类也就越来越多了。第二种方式,随着我们的接口越来越多,代理类也就越来越复杂,同时对于单个接口的实例化对象来说,其他接口功能是没有半点作用的,因此这两种方式都是不可取的。这个时候,动态代理也就应运而生。

动态代理

    动态代理,顾名思义,就是动态生成代理类的一种代理模式,动态生成的意思就是,在JVM运行的过程中,JVM自动生成某个接口的代理类并实例化代理对象,可能说的还是比较抽象,下面通过代码来详细讲解动态代理的思想。动态代理主要是运行Java中的反射机制,Spring AOP的实现也就是依靠动态代理的思想。

    学习动态代理,不可避免的要用到下面这两个对象。

    1、InvocationHandler接口

    从Handler这个单词就可以看出是处理的意思。我们使用动态代理的时候,需要在原有接口功能的基础上扩展某些功能,而这某些功能的实现,就是放在InvocationHandler.invoke(Object proxy,Method method,Object[] args)中实现,这个invoke方法存在三个参数,proxy 表示代理对象,method 表示被代理的真实对象的方法,args表示方法对应的参数,当通过下面的newProxyInstance生成一个代理对象proxy的时候,无论调用proxy中任何方法,它底层都是调用InvocationHandler.invoke方法,这个method参数就是proxy代理对象调用的方法

    2、Proxy类

    从名字上看,这个单词就知道是代理的意思。其主要功能就是动态的生成代理类对象。一般使用到的方法是它的静态方法newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)。

    下面将通过具体demo来讲解动态代理的原理

    1、定义功能接口(这里为了突出动态代理的效果,拿两个功能接口为例)

    代理模式(Proxy)

    代理模式(Proxy)

    2、真是对象类

    代理模式(Proxy)

    3、自定义InvocationHandler并重载invoke方法(运行真实对象函数并扩展功能,为代理对象服务的接口)                                             代理模式(Proxy)

    4、根据真实对象+接口+InvocationHandler动态生成代理对象

代理模式(Proxy)

    由上图,我们可以总结出如下几点结论:

  • Proxy.newProxyInstance的参数分别为类加载器、真实对象实现的所有接口以及对应的代理InvocationHandler。
  • 每个代理对象,只能代理真实对象的某一个接口。(这点参考静态代理)
  • 同一个真实对象的代理对象的类名称相同。(样例IActionA和IActionB对应的代理对象中都是$Proxy0)
  • proxyObj代理对象它是JVM动态生成的,既不是接口对象实例也不是真实对象实例。
  • 代理对象调用任何方法,其实都是调用对应的InvocationHandler.invoke方法,上图中例子proxyObjA.doSomethingA1()方法调用,实际上是首先运行invoke方法中的System.out.println("我能够帮忙所有不同类型的接口完成统一功能");语句,再运行method.invoke(realObj, params);语句,其中这个method就是doSomethingA1(),对应的对象是realObj,没有参数。

    综上:动态代理模式,其实底层原理跟静态代理是一样的,需要注意点有。第一、代理三要素跟静态代理相同,功能接口+真实对象+代理对象。第二,每个动态代理对象规定只能代理真实对象的某一个接口。(多个接口需要实现多个代理对象)。第三、一个真实对象(即使多个接口)所对应的代理对象名称是一样的$Proxy0。最后,我们要明白通过代理对象运行的方法,底层都是调用对应的InvocationHandler.invoke方法。