@lxx3061313
2017-08-07T02:24:47.000000Z
字数 2764
阅读 195
代理模式,java代理
代理模式在生活中随处可见,每当过年回家的时候我们总会想起那些奋斗在一线的火车票黄牛,他们是那么的亲切和温暖,可以轻轻松松的帮助你完成回家的梦想。那么同样的,在平常的开发工作中,我们总会遇到为一个对象提供代理对象的需求。因为在某些情况下,一个对象可能不合适或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
在Java中代理有两种模式:静态代理和动态代理。
静态代理的实现方式比较直接,可以描述为:
创建一个接口,然后创建被代理类来实现该接口,并且实现接口中的抽象方法。之后再创建一个代理类,同是也实现这个接口。在代理类中持有一个被代理对象的引用,而且在代理类方法中调用该对象的方法。
上面这段话比较绕,但是确实说出了静态代理的实现方式。下面按照上面的描述做一个简单的实现:
1.创建一个接口
2.创建被代理类
3.创建代理类
4.测试调用
在静态代理中,一个委托类对应一个代理类,代理类在编译期间就已经确定了。这种方式的缺点非常明显,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
动态代理解决了上述静态代理的问题,所谓动态就是代理类在运行时生成,相比静态代理,动态代理可以很方便的对委托的方法进行统一处理。动态代理有两种实现方式jdk动态代理和cglib动态代理。本文先讲解jdk动态代理的实现。在接下来的内容中同学们会彻底理解jdk动态代理的本质。
同样的为了理解,我们先简单写个小程序。
1.创建接口
2.创建被代理类
3.创建统一代理类
4.测试调用
上面例子同静态代理不同的地方在于第三步,统一代理类的实现。可以看到CommonProxy实现了一个接口InvocationHandler,那么什么是InvocationHandler呢?
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
从上面的官网描述中我们可以抓住一个重点,当proxy instance触发一个调用,这个调用会通过某种方式传递到invocation handler的调用方法中。然后由invocation handler去完成最终被代理类的调用。
那么这里有两个核心的问题必须解决
在CommonProxy的实现中,通过内部的getProxy()来获得代理实例。而getProxy()内部是通过
Proxy.newProxyInstance(loader, interfaces, this);
来完成代理实例的创建。那我们看一下newProxyInstance完成了什么工作:
分析一下上图的代码,我们看到其核心主要完成了两项工作
1.Look up or generate the designated proxy class.
2.Invoke its constructor with the designated invocation handler.
我们知道在Java体系中,要想创建一个类实例,首先要有一个类的定义文件(字节码文件)。在上述两个步骤中,第一步即完成了类文件的创建。下面我们重点看下类文件怎么生成的。即
Class<?> cl = getProxyClass0(loader, intfs);
做了啥?
proxyClassCache是一个WeekCache,定义如下:
我们暂且不管为什么这个Cache叫WeekCache,从其定义的结构上看,它是一个结构的缓存,缓存的内容从定义来看,就是缓存了每个classLoader所加载的Class。因为动态代理的代理实例是在运行期完成创建,所以肯定是不在这个缓存中的。那么就会通过ProxyClassFactory来创建。由于ProxyClassFactory代码过长,这里我只贴出核心代码,有兴趣的同学可以去一下源码。
在ProxyClassFactory主要做了两个工作:
1.生成字节码文件,通过ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)完成。
2.加载字节码文件并解析成Class对象,通过defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length)完成。
字节码的生成方法和解析工作不是本文的重点,在此不表。那么我们接下看一下生成的是一个什么样的Class。上述源代码提供的方法,我们来生成一个代理类:
程序运行完会在指定目录下生成Class文件
代理类名叫1.class.然后我们直接用IDE打开这个Class文件,可以看到反编译后的结果。
从反编译后的结果,我们可以得出以下信息:
1.代理类的名称为1
2.代理类继承了Proxy类,并实现了Service(自定义)接口。
3.根据父类信息,代理类定义了四个成员变量,都是Method类型,其中m3为Service中的service方法。
4.在代理类内部依赖了InvocationHandler,也就是完成了代理类跟目标类的绑定。
5.在调用阶段直接调用了InvocationHandler的invoke方法。
6.在代理类内部依赖了InvocationHandler。
7.InvocationHandler后面的内容可以参考上面的解释。
本文主要讲解了Java代理的两种模式:静态代理和动态代理。静态代理是比较简单,缺点也比较突出。为了弥补静态代理的缺点,这里引出了动态代理。在动态代理部分我们主要解释清楚了动态代理的两大核心工作
1.运行时创建代理类
在运行时,jdk为根据目标接口(Service)信息,生成代理类的字节码文件,并通过制定的Classloader来完成字节码文件的加载和解析,并返回一个代理类的Class。最后通过该Class的构造函数完成代理类实例的创建。
2.调用怎么传递到我们自定义的InvocationHandler
从生成的代理类Class可以看出,在代理类内部完成了对InvocationHandler的绑定。那么调用就可以顺利成章的传递到相应的InvocationHandler中。那么InvocationHandler的引用在什么时候传递进来的呢??请同学们自己分析一下。