什么是代理?
为什么用代理?
代理的方式有哪些?优缺点有哪些?
1、 简述
代理模式是Java 23种设计模式之一。代理分为静态代理和动态代理。所谓代理,即是通过对其他对象提供代理以控制这个对象访问。
使用一个代理对象将对象封装起来,然后使用该代理对象来取代该对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时调用原始对象的方法
1.1 静态代理
静态代理的实现方式有两种:a)通过与委托类实现同一接口;b)通过继承委托类。
这两种方法正好对应动态代理的两种方式的实现原理。我们在后续讲解动态代理的时候,再详解该部分。
1.2 动态代理
首先,我们 先存在一个疑问,既然存在静态代理了,为什么又要存在动态代理呢?首先,静态代理,需要开发人员(程序员)编写,在运行期间不需要其他消耗。存在动态代理,就是因为如果存在多个委托类,就需要开发人员手动编写多个代理类,这样就会存在很大的工作量,那为什么不能由Java自己动态生成代理类呢,这就是动态代理存在的理由。
动态代理实现的方式也有两种:a)JDK原生的代理;b)cglib方式。
所谓动态代理,即是在运行时自动生成代理类的方式。而静态代理类是在编译期间已然创建。
1.3 为什么要代理?
当在某个成熟类提供的功能之外,扩展补充功能时,就可以选择代理模式。比如:
1)权限控制,在某项业务类,只有通过安全校验的用户才能访问(调用)这个类,此时就可以选择做一个该类的代理类,要求所有的请求必须通过该代理类,由该代理类做权限判断。
2. 代理的用法
简述中提到,代理分为动态代理和静态代理,动态代理和静态代理又分别有两种用法,那下面我们先讲讲静态代理的用法。
2.1 静态代理
静态代理的第一种用法,就是与委托类实现同一接口,代理类持有委托类的实例,实际逻辑还是由委托类完成。我们以电影演员(MovieActor)与经纪人(Agent)为例。
1) 首先定义一个接口,Actor

2) 定义电影演员类,实现Actor接口

3) 定义经纪人类(Agent)(即MovieActor的代理类),我们取名为ActorProxy.同样实现Actor类

4) 至此,静态代理的第一种方式我们已经编写完成了,让我们测试一下吧。


第二种方式,是采用继承的方式实现的。还是同样的例子:
1) 定义艺人类Actor

2) 定义经纪人类ActorProxy,继承艺人类(Actor)

3) 至此,第二种方式我们也完成,让我们测试下吧。


至此,静态代理的两种方式我们已经讲解完成。
2.2 动态代理
动态代理同样分为两种方式,jdk动态代理和cglib代理。为什么动态代理要有两种方式呢?因为jdk代理的委托类需要实现接口,即jdk代理针对于接口,若委托类未实现任何接口,则无法使用动态代理类。这也是cglib代理存在的理由,因为cdlib可以针对接口,也可以针对继承类,即委托类只要有父类或父接口即可。至于为何jdk只能使用接口方式,我们后续会分析其原理。
Ok,下面我们先开始讲解jdk代理。还是同样的例子
1) 首先我们还是先定义艺人接口 Actor

2) 定义电影艺人类 MovieActor,使其实现艺人接口Actor.

3) 定义艺人演出代理调用处理类ActorInvocationHandler,该类需实现jdk的InvocationHandler接口。

通过上述截图,我们可以看到,我们在实现InvocationHandler接口,需实现其invoke方法,该方法有3个参数,分别为代理类对象,代理的方法及代理的方法参数。代理调用处理器是通过反射实现的。
4) 因为动态代理类是在运行时自动生成的,那么我们直接来测试吧。

上述截图中,代理对象是通过jdk的Proxy类生成。我们调用Proxy类的newProxyInstance方法,传参分别为委托类的类加载器、委托类的父接口数组及代理的调用处理器实例。在调用代理类的方法时,代理类会将方法调用传递到invoke方法,运行结果如下:

如果我们再定义TV Actor,同样采用上述初始化代理类的方式即可。


那既然是在运行时生成代理类,我们可以看到生成的代理类吗?当然可以,只要在初始化代理类之前,设置参数:
sun.misc.ProxyGenerator.saveGeneratedFiles 为 true

设置完成后,会保存代理类,类名为$Proxy+数字,存储位置在当前工程目录下的com/sun目录下。
我们分析下生成的class文件,看下反编译后的源码:
a)可以很直接的看到代理类继承于Proxy,实现于在Actor(在初始化阶段传入的接口数组)

b)在反编译后文件中存在4个Method类型的类变量,这里为什么会有4个呢?不要着急,后面我们会看下这4个类变量分别代表的什么,就明白了。

通过下面的静态块代码,我们可以看到m1、m2、m3、m4代表的含义,即从Object继承来的3个公共方法和从Actor实现的show方法(即我们需要代理的方法)

我们详细的看下show方法是如何实现的

通过代码我们可以,方法的最终实现是调用的代理调用处理器的invoke方法
c)继续看class文件,存在一个参数类型为InvocationHandler的构造方法,方法内部将InvocationHandler参数传递给父类Proxy

我们可以看到Proxy类中存在InvocationHandler类型的变量,这个代表的就是我们编写的代理调用处理类。

5) 我们通过时序图分析下动态代理类的生成原理,时序图如下(后续补充):
至此,jdk动态代理我们已经分析完成。可以看出jdk动态代理和静态代理的第一种方式实现方式是一致的。后面我们会继续分析cglib动态代理。cglib代理方式需引入相关jar包,我们采用maven管理项目工程,可直接在maven 仓库中搜索相应的版本。存在父接口和不存在父接口的cglib均可以使用,这是与jdk代理的不同之处。