终于有人把 java 代理 讲清楚了,万字详解!
什么是代理
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
代理其实不仅仅是在软件开发领域,在我们的日常生活中也是时常可见。比如某p2p老板突然携款带着小姨子跑路了,可怜了下面一堆的程序员背负一身房贷,上有老下有小,程序员只能被迫去申请劳动仲裁,劳动局就会为其指派一位代理律师全权负责程序员的仲裁事宜(PS:p2p跑路仲裁拿回工资的可能性非常低,没让你把工资退回就算好的了)。那这里面就是使用了代理模式,因为在劳动仲裁这个活动中,代理律师会全权代理程序员。比如:房东要将房子出售,于是到房地产中介公司找一个中介(代理),由他来帮房东完成销售房屋,签订合同、网签、贷款过户等等事宜。
代理模式
这是常见代理模式常见的 UML 示意图。
需要注意的有下面几点:
用户只关心接口功能,而不在乎谁提供了功能。上图中接口是
Subject
。接口真正实现者是上图的
RealSubject
,但是它不与用户直接接触,而是通过代理。代理就是上图中的
Proxy
,由于它实现了Subject
接口,所以它能够直接与用户接触。用户调用
Proxy
的时候,Proxy
内部调用了RealSubject
。所以,Proxy
是中介者,它可以增强RealSubject
操作。
代理又可以分为静态代理和动态代理两种。我们先来看下静态代理。
静态代理
电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如提供按摩椅,娃娃机(这个每次去电影院都会尝试下,基本上是夹不起来,有木有大神可以传授下诀窍),卖爆米花、饮料(贵的要死,反正吃不起)等。我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?然后在影片开始结束时播放一些广告。
下面我们通过代码来模拟下电影院这一系列的赚钱操作。
首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie
,代表电影播放的能力。
接下来我们要创建一个真正的实现这个
Movie
接口的类,和一个实现该接口的代理类。
真正的类《美国队长》
电影:
代理类:
测试类:
运行结果:
现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。这个就是是静态代理的内容,为什么叫做静态呢?因为它的类型是事先预定好的,比如上面代码中的 MovieStaticProxy
这个类。
优点
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
代理对象可以扩展目标对象的功能
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度。
缺点
代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
jdk动态代理
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect
包中的Proxy类和InvocationHandler
接口提供了生成动态代理类的能力。
接着上面的例子,刚看完《美国队长》不过瘾,还想继续去看一场《钢铁侠》。一直在普通影厅看电影觉得没啥意思,那就赶紧去VIP影厅(
至今不知道长啥样子
)体验一把。既然 实体店没体验过那就用代码来体验一次吧。创建一个VIPMovie电影接口
紧接着创建一个VIP影厅的播放实现类
如果按照静态代理我们是不是又要创建一个VIP影厅播放的代理实现类,这种方式我们就不演示了。下面我们来看看通过动态代理怎么来实现吧。
MyInvocationHandler
实现了 InvocationHandler
这个类,这个类是什么意思呢?大家不要慌张,下面我会解释。然后,我们就可以在VIP影厅看电影了。
输出结果:
看到没有,我们并没有像静态代理那样为 VIPMovie
接口实现一个代理类,但最终它仍然实现了相同的功能,这其中的差别,就是之前讨论的动态代理所谓“动态”的原因。
我们顺带把《美国队长》也用动态代理实现下吧。
输出结果:
**我们通过 Proxy.newProxyInstance()
方法,却产生了 Movie
和 VIPMovie
两种接口的实现类代理,这就是动态代理的魔力。**
JDK动态代理到底是怎么实现的呢
动态代码涉及了一个非常重要的类 Proxy
。正是通过 Proxy
的静态方法 newProxyInstance
才会动态创建代理。具体怎么去创建代理类就不分析了,感兴趣的可以去看下源码。我们直接看下生成的代理类。
如何查看生成的代理类?
在生成代理类之前加上以下代码(我用的jdk1.8):
代码如下:
产生了两个代理类分别是$Proxy0
和$Proxy1
。
下面们来看下"钢铁侠"的代理类$Proxy0
通过上述代码我们可以看到 $Proxy0 extends Proxy implements VIPMovie
继承了Proxy
且实现了VIPMovie
接口,这也就是为什么jdk动态代理必须基于接口,java 是单继承的。
然后再看下代理类实现的方法:
这个supper.h.invoke Proxy
中的h的invoke方法,即InvocationHandler.invoke
也就是上面 MyInvocationHandler.invok
e方法,至此整个流程就清晰了。这就是jdk的动态代理。
cglib动态代理
上面说jdk动态代理只能基于接口,那么如果是类要动态代理怎么办呢?cglib动态代理就可解决关于类的动态代理。
下面我们来创建一个“《美国队长2》”
引入cglib pom依赖
创建一个自定义MethodInterceptor。
测试类
输出结果:
我们看下最终创建的代理类生成的play
方法
从代理对象反编译源码可以知道,代理对象继承于CaptainAmerica2MovieImpl
,拦截器调用intercept
()方法,
intercept
()方法由自定义CglibProxyInterceptor
实现,所以,最后调用CglibProxyInterceptor
中的intercept
()方法,从而完成了由代理对象访问到目标对象的动态代理实现。
CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
用CGlib生成代理类是目标类的子类。
用CGlib生成 代理类不需要接口。
用CGLib生成的代理类重写了父类的各个方法。
拦截器中的intercept方法内容正好就是代理类中的方法体。
总结
代理分为静态代理和动态代理两种。
静态代理,代理类需要自己编写代码写成。
动态代理有jdk和cglib,代理类通过
Proxy.newInstance()
或者ASM
生成。静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
动态代理通过 Proxy
动态生成 proxy class
,但是它也指定了一个 InvocationHandler
或者 MethodInterceptor
的实现类。
代理模式本质上的目的是为了增强现有代码的功能。
结束
由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
感谢您的阅读,十分欢迎并感谢您的关注。
参考
https://blog.csdn.net/m0_37314675/article/details/77850967
https://www.cnblogs.com/cC-Zhou/p/9525638.html
https://www.jianshu.com/p/4539e6d9f337
版权声明: 本文为 InfoQ 作者【java金融】的原创文章。
原文链接:【http://xie.infoq.cn/article/9a9387805a496e1485dc8430f】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论 (6 条评论)