静态代理VS动态代理:代码增强的艺术
本文探讨了代理模式的两种实现方式:静态代理和动态代理。静态代理针对特定接口进行方法增强,实现简单但缺乏复用性;动态代理通过InvocationHandler实现通用代理,可复用但对接口有依赖。文章以线程池拒绝策略报警功能为例,分析两种代理的适用场景,并指出静态代理更适合单一功能的扩展。同时解答了JDK动态代理为何只能代理接口的问题,并解释了MyBatis无需实现类即可动态代理的原理——直接解析接口
静态代理:对某个具体接口做特定的加强!
动态代理:只要一个实现类,实现了任意一个接口,并且想对这个接口里的方法进行增强,
只需要一个代理对象,就可以统筹兼顾所有这样的实现类。
这个代理对象的职责一般是单一的,用来打日志,或者鉴权啥的。
能够在不侵入源代码的情况下,做到对实现类接口方法的增强
引言:
我们从一个例子引入:
我们现在有一个bigstar,实现了star接口。
star接口内定义的就是我们bigstar所需要加强的方法。
star接口内有一个sing的方法,如下


但是我们bigstar在唱歌之前还需要一个人来帮我准备场地,但是我又不想动star的代码,大明星怎么可能会来准备场地呢。我们需要一个代理。
静态代理
具体代码怎么写呢?
只需要一个代理,也实现star接口来增强方法就可以了
(streambuffer流中就是用的这一套)



可以看到,我们在没有改变Bigstar的代码下,成功实现了准备场地的增强方法。
而且这个代理类不仅能够对Bigstar起作用,对任何一个实现了star接口的方法都能够起作用。
静态代理的优点:简单,编译时就已经确定,无性能损耗
缺点-->假设有另一个舞者来的话,不能复用创建场地的代码,只能为舞者单独写一个创建场地的代理
如果有100个人想要创建场地,就需要写100个代理类,会让类爆炸!
动态代理应运而生!
动态代理三要素:
实现类
接口
InvocationHandler(动态代理处理器)
接口、实现类和静态代理一样,我们已经有了star和bigstar

注意,这里的target是object类型,target的意思是我们需要代理的对象,比如bigstar


运行结果是一样的。
动态代理这样使用,可以看到代码相对复杂。
但是动态代理的好处就是可以复用,比如说要新来一个舞者,只需定义一个接口一个具体类,将star改为dancer,bigstar改为bigdancer即可
例1
--现在来到我们的项目之中,该如何完成线程池策略报警
目的:在原有用户自定义线程池拒绝策略下,增加报警功能(比如说输出到日志上)

可以看到,reject方法被final修饰,不能重写。
但是很巧的是,下面的handle(线程池拒绝策略处理器)是一个接口,已有四种线程池拒绝策略的实现类!
我们需要对其进行加强,补上报警功能。
代理模式呼之欲出!
静态代理 or 动态代理?
由于是单纯对线程池拒绝策略的拓展,并不会涉及到其他的东西,相当于我们只对bigstar进行服务,使用静态代理显然是更好的选择!
可以类似我们上面给bigstar的静态代理,写出一个样板代码
注意,我们要代理的实际上是RejectedExecutionHandlr,这里对线程池做继承是为了拓展线程池的方法(其实也是一种代理方式,利用继承来做代理)

代理类接口,在调用原来的拒绝策略处理器前先调用beforereject



测试没有问题,最大线程数加上阻塞队列长度为5,拒绝3次,所以说刚好是两次拒绝。
为什么要选择override set方法,而不是直接如同我们之前的静态代理(装饰器模式)一样,在原有的处理器上做包装?就像这样

原因:我们要做的是动态线程池,需要支持可以动态调整拒绝策略,所以说需要重写set方法,让我们后面可以更换。

根据新的配置文件中的rejecthandle来进行更换。
--------------------拓展1-----------------
lambda表达式实现静态代理
(这个代理类是匿名的,如果只会在这里被使用可以做成匿名函数更加简单)

可以直接省去定义新的代理对象的步骤,直接用lambda表达式,更简洁明了。
--------------------拓展2---------------------
JDK 动态代理为什么不能对类进行代理,只能对接口做代理?
原因:因为代理对象已经继承了Proxy类,无法再继承我们想要的类(像SupportRejectPoolExecutor那样)
同理:抽象类也是类,做不了代理
mybatis没有实现类,怎么做到的动态代理?
前面提到,动态代理的三要素为:
实现类
接口
InvocationHandler(动态代理处理器)
而在实际应用中,我们往往是通过mapper接口加上注解上写sql来实现的。
mybatis没有实现类,怎么做到的动态代理?
可以注意到,我们是有接口的,直接对接口上的注释提取拿到sql,新建一个代理对象,但是这个代理对象不需要调用实现类的方法,只需要解析sql,调用jdbc,解析数据库传回来的结果便可。整个流程中不需要实现类。
第一次写博客,大家多多海涵。
更多推荐



所有评论(0)