在后面学习的时候,发现自己对于动态代理的概念又不记得了,就再次回头
什么是代理?
所谓代理就是代替处理
代理模式可以带给我们什么?
我们需要高内聚低耦合
解耦
高扩展性(我们所有的模块应该都要具有高可扩展性!)
一、 静态代理
实现案例
我们用外卖小哥的案例来举例
① 创建一个用户,他需要进行点餐
package com.wxy.proxy;
public class Customer {
public void order(String orderName){
System.out.println(orderName);
}
}
②创建一个外卖员,帮助商家进行配送等操作
package com.wxy.proxy;
public class DeliveryMan extends Customer{
@Override
public void order(String orderName) {
super.order(orderName);
System.out.println("外卖小哥正在配送");
System.out.println("请耐心等待。。。");
System.out.println("外卖小哥帮你搅拌搅拌");
}
}
③main方法
package com.wxy.proxy;
public class StaticProxy {
public static void main(String[] args) {
Customer customer=new DeliveryMan();
customer.order("点餐ing");
}
}
最后的运行结果为
分析总结
使用静态代理我们会发现会存在很多问题,就是当我们的代码很多的时候,或者说被代理类方法多的时候,我们使用代理类进行维护的时候需要修改的东西很多很多!!!或者换一种说法就是我们的静态代理需要重写很多代理类已经有的方法【这里指使用接口实现的时候!】
如果我们此时只用增强一个方法,但是使用静态方法就需要给这一个方法增强的时候还要把别的方法全部抄下来,这难免就使得整个方法复杂了很多很多!!
那么我们需要怎么做?
这个时候动态代理的好处就体现出来了
二、动态代理
什么是动态代理?
1. 基于jdk的动态代理
#### 动态代理
动态代理技术,是在内存中生成代理对象的一种技术。也就是整个代理过程在内存中进行,我们不需要手写代理类的代码,也不会存在代理类编译的过程,而是直接在运行期,在JVM中“凭空”造出一个代理类对象供我们使用。一般使用的动态代理技术有以下两种:
##### 基于JDK(接口)的动态代理
JDK自带的动态代理技术,需要使用一个静态方法来创建代理对象。
##### 它要求被代理对象,也就是目标类,必须实现接口。
生成的代理对象和原对象都实现相同的接口,是兄弟关系。
```java
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
``` 主要关注参数列表:
ClassLoader loader:固定写法,指定目标类对象的类加载器即可。用于加载目标类及其接口的字节码文件
通常,使用目标类的字节码对象调用getClassLoader()方法即可得到
Class<?>[] interfaces:固定写法,指定目标类的实现的所有接口的字节码对象的数组
通常,使用目标类的字节码对象调用getInterfaces()方法即可得到
InvocationHandler h:
这个参数是一个接口,主要关注它里面唯一一个方法,invoke方法。它会在代理类对象调用方法时执行,也就是说,我们在代理类对象中调用任何接口中的方法时,都会执行到invoke中。所以,我们在此方法中完成对增强或者扩展代码逻辑的编写。
```java
Object invoke(Object proxy, Method method, Object[] args)
``` proxy:就是代理类对象的一个引用,也就是Proxy.newProxyInstance的返回值,此引用几乎不回用到,忽略即可。
method:对应的是触发invoke执行的方法的Method对象。假如我们调用了xxx方法,该方法触发了invoke的执行,那么,method就是xxx方法对应的反射对象(Method对象)
args:代理对象调用方法时,传递的实际参数
总结:
基于接口的动态代理,实际上是在内存中生成了一个对象,该对象实现了指定的目标类对象拥有的接口。所以代理类对象和目标类对象是兄弟关系。
##### 兄弟关系:并列的关系,不能互相转换,包容性比较差。在后续会学习Spring框架,如果配置JDK的动态代理方式,一定要用接口类型接收代理类。
2. 基于CGLIB的动态代理
##### 基于CGLIB(父类)的动态代理
第三方CGLIB的动态代理技术,也是可以使用一个静态方法来创建代理对象。
##### 它不要求目标类实现接口,但是要求目标类不能是最终类,也就是不能被final修饰。
因为CGLIB是基于目标类生成改类的一个子类作为代理类,所以目标类必须可被继承。
```java
Enhancer.create(Class type, Callback callback)
``` 主要参数列表:
Class type:指定我们要代理的目标类的字节码对象,也就是指定目标类的类型
Callback callback:此单词的意思叫做回调,意思就是我们提供一个方法,它会在合适的时候帮我们调用它。回来调用的意思。
Callback是一个接口,由于该接口只是一个名称定义的作用,并不包含方法的声明。所以我们使用时通常使用它的一个子接口MethodInterceptor,此单词的意思叫做方法拦截器。
MethodInterceptor接口中也只有一个方法,叫做intercept
```java
Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
``` proxy:就是代理类对象的一个引用,也就是Enhancer.create的返回值,此引用几乎不回用到,忽略即可。
method:对应的是触发intercept执行的方法的Method对象。假如我们调用了xxx方法,该方法触发了intercept的执行,那么,method就是xxx方法对应的反射对象(Method对象)
args:代理对象调用方法时,传递的实际参数
methodProxy:方法的代理对象,一般也不作处理,可以暂时忽略
总结:
基于父类的动态代理,是在内存中生成了一个对象,该对象继承了原对象(目标类对象)。所以代理类对象实际上是目标类对象的儿子。
##### 父子关系:父子关系,代理类对象是可以用父类的引用接收的。
### 总结
1. 代理模式在Java开发中是广泛应用的,特别是在框架中底层原理经常涉及到代理模式(尤其是动态代理)
2. 静态代理和动态代理,实际使用时还是动态代理使用的比较多。原因就是静态代理需要自行手写代码,维护、修改非常繁琐,会额外引入很多工作量。也不能很好的使用配置完成逻辑的指定,所以使用较少。
3. 基于JDK和基于CGLIB,实际使用时两个都会用到。
1. 在spring中,默认情况下它就支持了两种动态代理方式。如果你指定的目标类实现了接口,spring就会自动选择jdk的动态代理。而如果目标类没有实现接口,则spring会使用CGLIB。
2. 我们在开发时,由于基于JDK的动态代理要求比较多,更不容易实现,所以很多人习惯于统一配置为使用CGLIB进行代理。也就是CGLIB更通用。
3. 如果使用dubbo+zookeeper,底层进行代理时,最好配置定死使用CGLIB的方式进行代理。因为dubbo会使用基于包名的扫描方式进行类的处理,而JDK的代理类生成的包名类似于com.sun.proxy....格式。我们实际需要让代理类和目标类保持一样的包名,所以只有CGLIB能保持原包名不变生成代理类。