JAVA中对类进行增强的方式大致有三种
1、继承;
2、装饰者;
3、代理模式。
继承是最基础的,也是面向对象的三大基本特征之一。优点是逻辑简洁清晰,缺点是目标对象和增强部分固定,高度耦合。
而装饰者模式对继承进行的改善。“是你还有你,一切依赖你“:与被增强对象继承同一父类且内部持有被增强对象实例。运行期间对对象进行增强,对目标对象进行了解耦,但增强内容是不能改变的。
代理模式进一步对增强内容进行的解耦,使其更加灵活。
(1)静态代理:实现较为简单,具体可以理解为编写一个代理类,实现目标对象相同接口,并在内部维护一个对象的引用。然后对增强方法进行复写。
注意区分:静态代理和装饰者模式比较像,但静态代理是编译时期就进行了对象增强,而装饰者模式则是在运行期间增强。
(2)动态代理:在运行时创建一个实现某些给定接口的新类(也称"动态代理类")及其实例。
使用动态代理首先要熟悉两个类:java.lang.reflect包下的Proxy、InvocationHandler
Proxy:
![835126d5eb78bf41aecebc8ade5cb2b1.png](https://i-blog.csdnimg.cn/blog_migrate/775800413f05af553662124a54775ec5.png)
Proxy中有一个静态方法newProxyInstance,可以通过三个参数——类加载器、接口、和InvocationHandler来返回一个代理对象实例。
InvocationHandler:
![8cbf0c4d8f50c9425282ed5bb40773bd.png](https://i-blog.csdnimg.cn/blog_migrate/daa4e74fbf1619dd93c37bec36c71a36.png)
InvocationHandler是一个接口,只有一个函数invoke。其中包含了三个参数,从名字看得出来第二参数是方法对象。
newProxyInstance实验:
类加载器:
![d28baf92aa209157bd143d50a80fae34.png](https://i-blog.csdnimg.cn/blog_migrate/3e3edf484124f27eb4fb4da4dd221ab4.png)
接口:
![6df3ee2568e74f482404c5ac90b304dd.png](https://i-blog.csdnimg.cn/blog_migrate/22c32854643c0ad85e5dc5eb62db3261.png)
InvocationHandler:
![e7767dfa7117c78702ceee7130e50020.png](https://i-blog.csdnimg.cn/blog_migrate/6e8497ac3661d549207e6490c829ceb6.png)
结果:
![ba4b54f51fcdfc4c7ea9a03b883c98dd.png](https://i-blog.csdnimg.cn/blog_migrate/a3eea8189e6a9b79a685f64558cc3d19.png)
目前结论:
1. 调用newProxyInstance会生成一个包含$Proxy7的一个代理类;
2. 使用接口方法会调用InvocationHandler的invoke。
InvocationHandler参数实验:
interfaceA中添加有变量的函数声明:
![a887a8907212dafac5352c287c4d7e91.png](https://i-blog.csdnimg.cn/blog_migrate/7b818391e1c3e44832909da9c24b8ff9.png)
再到InvocationHandler的invoke方法复写
![96b4584b3205a52eb60d42c2ef20b446.png](https://i-blog.csdnimg.cn/blog_migrate/4d7d98ebf322635c76c30987e6fe4df1.png)
![05de6046df0838e77c21d45851df4b36.png](https://i-blog.csdnimg.cn/blog_migrate/11ffa250a95c8230f38c5f371d4018b4.png)
结果:
![b9393d3b056b1206fd72a6082987bf79.png](https://i-blog.csdnimg.cn/blog_migrate/335f7b1eeb316cfef1d261125c57cf6e.png)
可以得出结论:InvocationHandler中第一个参数是代理类、第二个参数是方法、第三个参数是方法中传入的参数。
小结:proxy的newProxyInstance方法可以通过类加载器、接口、和InvocationHandler三个参数创建一个实例对象。对象可以根据接口类型进行多态转换,调用方法。调用方法时调用InvocationHandler中的invoke方法。
目的:对目标对象进行增强,所以目标对象也要传入。
新建一个Person对象,实现A接口
![b70384bb3747585a85c136c3add84010.png](https://i-blog.csdnimg.cn/blog_migrate/2c2871d901357b549025086081976179.png)
动态代理中传入Person的实例,且在invoke中调用
![952ae14703f8d6a9267db5305bb29e74.png](https://i-blog.csdnimg.cn/blog_migrate/3a0a8b754d69dba0f0afa69d2b4b1c1b.png)
进行调用
![70b29d7cd0aad5aee0e2d3978cf471aa.png](https://i-blog.csdnimg.cn/blog_migrate/5e7f243c9ba6a6ea9ddd5debb0c136a6.png)
结果
![c4154bad4b6a188a2c07baf2849c1712.png](https://i-blog.csdnimg.cn/blog_migrate/348d237520a50a53a84ac025e4409712.png)
目前问题: 增强方法是固定的,修改需要更改内部代码。
解决方法:将增强方法方法传参。
第一步:预设增强方式分为前置增强和后置增强,创建beforeAdvice和afterAdvice两个接口。
![0304a31a7f2518f07925a429036f194a.png](https://i-blog.csdnimg.cn/blog_migrate/ccdc9e773c941b47151eea9abb6ccc32.png)
使用工厂模式来返回增强的对象,其中工厂内需要beforeAdvice和afterAdvice还有目标对象三个参数。
第二步:创建ProxyFactory的类,里面包含beforeAdvice、afterAdvice、targetObject
![abb4f3aec647ba8a0a76536cd5f39494.png](https://i-blog.csdnimg.cn/blog_migrate/85ff3aae65579ed24db20e5b1d99df9e.png)
第三步:写createObject函数返回一个增强对象,
![6e83330634750b8aa8ea4448cc0b5eca.png](https://i-blog.csdnimg.cn/blog_migrate/579fa69c552446f5af9ae3b13f4e8de9.png)
测试:
![6ee30d8eeb4754bc8c85ae3745e6199e.png](https://i-blog.csdnimg.cn/blog_migrate/d13c15dd61ec02b72924e8504a4f3a2c.jpeg)
结果:
![c6cfc2766ad804cfde922009387c3017.png](https://i-blog.csdnimg.cn/blog_migrate/28b2b44ef87438a7a285fe072aef3975.png)
此时就可以把增强方法和目标函数进行了解耦,动态返回了代理对象。