文章目录
0. 前言
动态代理是非常的厉害啊,想当初学习mybatis的时候,被 只写接口就能直接使用的情况 惊艳到了。当初想,为什么能这么厉害?这么神奇吗?哈哈,这就是动态代理的功劳了!非常的强大!。当然还有其他的,比如aop啊啥的。
1. 代理模式介绍
1.1 定义
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1.2 应用场景
- AOP(本文以此为例)
- 延迟加载: 一开始不去初始化“被代理对象”,用代理对象去代替“被代理对象”的位置,当用到的时候,代理对象在去初始化“被代理对象”,体现了“注重对对象的控制”
- 远程代理
- 等等等
1.3 分类
- 静态代理(感觉不经常用)
- 动态代理(比较重要)
1.4 与装饰模式的区别
-
代理:
- 注重对象的控制
-
装饰:
- 注重对象的增强
-
啥意思呢?
用装饰模式的情况下,你肯定是要用到被装饰对象的方法,在此方式下进行增强。且增强的与原来的功能一定是有密切联系的。
比如你用BufferedReader去装饰FileReadef。增强了其read方法,使其可以读一行,这增强的方法显然与被增强的方法有很强的关联。
反观代理模式中的扩展功能则不同 比如在方法的前面和后面打印日志,可以发现打印日志跟原来的方法没有很强的联系。并且装饰模式还可以层层装饰。其实总的来说,不用纠结了,基本上感觉用代理模式,也是去用动态代理,而纠结的是装饰模式与静态代理的区别。我就想问,动态代理能干的事儿,装饰模式行吗?
所以,就不用太纠结。代理模式下,什么时候创建 被代理对象? 代理实现的这个功能可能调用了被代理对象的一个方法,或者两个,或者有时间一个,有时候两个。所以来说,代理模式强调的是对象的控制。
2. 静态代理
静态代理,就是代理类是自己写死的,灵活性比较差
换句话说:在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就确定了。
2.1 场景介绍
场景很直白,如下:
我UserService 里面有一个save方法,用来保存的, 是几个月前写的。
现在需求变更来了,突然新来了一个需求,要求我们吧所有的UserService的save方法执行保存之前都要进行一个日志的记录。
那在不更改原来代码的前提下,就用到了代理模式(装饰模式也可以),代理一下原来的UserService对象,在UserService对象种的save方法执行之前,先输出日志。
2.2 类图
2.3 代码
-
step1 接口代码:
-
step2 被代理类代码:
-
setp3 代理类代码:
-
setp4 调用代码
2.4 优缺点
2.4.1 静态代理的优点
可以实现Client与被代理类类间的 解耦,在不修改被代理类代码的情况下能够做一些额外的处理。此优点代理模式都有,不管动态or静态
2.4.2 静态代理的缺点
缺点可想而知,从上面的代码可以的明显的发现。
缺点1: 代码重复,维护复杂 请看下图解释
缺点2: 存在类爆炸的可能
不知道有没有发现, 我对UserService进行了代理,我写了一个UserServiceProxy的一个代理类,那么在实际情况中,你可能就一个UserService ? 那不应该吧,你对UserService 的save方法进行增强。写了一个UserService的代理类,那么其他的Service的save 方法也得弄,那不就直接裂开了吗?😂
3. 动态代理(Jdk为例)
动态代理,就是呀,这个代理类,不是我们自己一开始写死的,而是动态生成的。非常的强,解决了上面静态代理的很多缺点。
注意一点,jdk的动态代理,被代理的类一定是要实现某个接口的。不然生成的代理类的时候 jdk不知道要对此代理类生成什么方法
(换个意思说,就是被代理类实现了某几个接口,那么你一定有这几个接口里面的方法。那么我jdk代理你这个类的时候,我也实现被代理类实现的这几个接口,我自然而然的就知道了你这个被代理类里面 一定有接口里面的这几个方法,那么我不就间接有被代理类中的方法了吗?)。
具体意思,(见3.4节)。
当然除了jdk的动态代理,还有cglib动态代理,这个cglib动态代理到是不用 被代理类实现什么接口。但是cglib也是要知道你有什么方法呀,所以cglib生成的代理类,是通过继承的方式来获得 被代理类的方法的。所以你被代理类一定不能是final类,你如果是final类,人家怎么通过继承 获得你里面有什么方法鸭。
3.1 代码
相信看这个的人,都会写jdk的动态代理,我就不介绍咋用了。
-
step1: 接口代码:
-
step2: 实现类代码:
-
step3: InvocationHandler代码:
-
step4: 工厂
为了解耦,我随便写了一个工厂。看情况写吧,不重要
-
step5 : Client代码
-
step6: 执行结果
3.2 优点
- 静态代理一个代理只能代理一种类型。动态代理是在运行时,通过反射机制实现代理对象的生成,并且能够代理各种类型的对象。意思看下图
- 接口中声明的所有方法都被集中到InvocationHandler.invoke()方法中去处理,大大提高了灵活性,指责更加单一。具体可看下图(方法来自InvocationHandler,详情看上面代码)
3.3 动态生成的代理类长什么样?
生成的代理类 长的什么样子?其内部执行的逻辑又是什么样子的?
我这边将代理类输出到了磁盘上,用Idea打开了。Idea可以自动反编译的。
其代码如下
3.4 生成的动态代理类执行分析。
生成的代理类是如何运作的?看下图 请按照数字标号看
3.5 “代理类生成” 的jdk源码执行过程(略) 有机会在写一篇
略略略略。
4. 总结
写这个花费了不少时间呢,总之可能看了这个,可能也不会懂什么,最主要的还是要自己体验吧。