前一阵子不是用AOP的时候碰见了一些问题嘛, 由于AOP是基于动态代理实现的,所以今天就抽时间看了一下~
代理(proxy)模式算是很经常用到的一种模式了。就像日常生活一样,自己做不到的一些事情可以找别人帮忙完成一下~比如上不去分了找代练、不想出去吃饭叫外卖 等等..
代理 说简单点就是四个字找人帮忙。在编码过程中具体体现为 通过低侵入的方式来实现一些功能
代理模式主要分为两种实现. 按照职责划分的话。可以大致分作8类:
- 远程代理
- 虚拟代理
- Copy-on-Write 代理
- 保护代理
- Cache代理
- 防火墙代理
- 同步化代理
- 智能引用代理
静态代理
通过硬编码实现。 需要代理的对象在程序运行前就是已知的。换句话说,就是写死了的代理~
比如说作为新生代优秀大学生, 部分人喜欢通过 智行 抢火车票而不是 12306, 但是我们知道网上的放票途径只有一种: 12306。 那么智行是怎么抢着票的呢? 它其实是拿着你的信息去找12306要票啦~(瞎猜的,不可确信)也就是说, 在这里智行扮演了个代理者的形象。我们可以直接到 12306 抢票,但是由于智行在抢票的基础上提供了更便捷的功能,所以也有不少人用智行。
这一过程可以用以下代码描述出来
示例
1 | /** |
1 | /** |
1 |
|
从上面的代码可以看到, 智行持有了 12306 这个对象, 当有人用智行的时候, 智行可以QWER一通乱秀后再去抢票, 同时惹出事后还能帮忙擦个屁股~ 上面的例子有点类似 AOP 中的 环绕增强(Around)
。 所以,当我们想在访问一个类时做一些控制或者其他花里胡哨的操作的时候可以试一试这个模式。
动态代理
上面的代码功能确实不错,但是写起来是真的累死个人…一次只能代理一个类。要是需要代理的类一多,来个超级加倍怕不怕? 更别说静态代理这个东西。写的太多了看起来确实不好看…
要知道
懒是人类的本质
懒是人类的本质
懒是人类的本质
(复读也是~)。
动态代理就因此而生了。当然,它和静态代理还是有一些不同的地方的。 动态代理是通过反射
创建,也就是说它是动态生成的。动态代理一般通过两种方式实现: JDK代理 和 CGLIB代理 两者底层实现不同, 我会尽力描述一下两者的特点和使用形式。
P.s: 在Spring 中的组件如果没有实现接口会使用 CGLIB代理,而对于已经实现了接口的组件会使用 JDK代理,可以Debug看一下地址,CGLIB代理的类会有CGLIB-xxxxxx
的标识.
JDK 代理
JDK代理 是基于反射和拦截器实现的,在我们代理一个类时,要确保满足以下条件:
- 这个类必须实现了某一接口
- 代理时需要实现
InvocationHandler
接口 - 需要用
Proxy.newProxyInstance
生成代理对象
JDK代理主要依赖于 java.lang.reflect包 中的 Proxy
和 InvocationHandler
这两个类。 依旧以抢火车票为例, 看一下是咋实现的:
上面的RobTicket
接口和_12306实现不用变
, 新增一个RobTicketInvocationHandler
类,这个类实现了InvocationHandler
方法,这个类现在的作用就相当于上面的ZhiXing
,帮忙抢票的~
1 | import java.lang.reflect.InvocationHandler; |
然后Main
方法也需要变动一下:
1 |
|
执行结果如下:
1 | 导入身份信息 |
可以看到已经成功进行代理了。
CGLIB代理
cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
上述是CGLIB 对自己的一个描述。CGLIB是一个很强大的代码生成类库,可以在运行期间扩展Java类与实现Java接口。在 Spring 中如果要对某一组组件使用 AOP 进行切入话,如果组件实现了接口,则会通过 JDK
进行代理,否则就会通过 CGLIB
进行代理。 也就是说,使用CGLIB代理的类可以不实现接口
下面还是通过代码来看一下怎么实现的
使用 CGLIB 需要引入依赖, 这里通过 maven 导入cglib3.2.5
:
1 | <dependencies> |
_12306
1 | public class _12306{ |
RobTicketMethodInterceptor
1 | import net.sf.cglib.proxy.MethodInterceptor; |
Main
1 | import cn.bestsort.code.proxy.cglib.RobTicketMethodInterceptor; |
执行结果如下:
1 | 导入身份信息 |
后记
我们在这里只说明了怎样进行静态代理和动态代理, 至于代理的具体步骤和源码分析, 本来想写的,后来发现篇幅过长。以后会专门写两篇文章尝试分析一下 JDK代理 和 CGLIB代理 的源码的