设计模式—proxy
什么是代理
增强一个对象的功能
买火车票,app就是一个代理,他代理了火车站,小区当中的代售窗口
java当中如何实现代理
java实现的代理的两种办法
代理的名词
代理对象 增强后的对象
目标对象 被增强的对象
他们不是绝对的,会根据情况发生变化
静态代理
继承 :代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂
聚合: 目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类爆炸,只不过聚合比继承少一点点
总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。
动态代理
自己模拟的动态代理
不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。
缺点:首先要生成文件
缺点:动态编译文件 class
缺点:需要一个URLclassloader
软件性能的最终体现在IO操作
JDK动态代理
通过接口反射得到字节码,然后把字节码转成class native openJDK c++
在自己模拟的动态代理中,代理逻辑是写死的,那么怎么让代理逻辑动态呢?
-
思考点1:将代理逻辑的方法传进来,传进来后怎么调用呢?method.invoke(object,args)么?注意一点,我们只是在newProxyInstance(Object
object)中去生成了代理类的源文件Xxx.java,
并不是通过newProxyInstance去调用了method.invoke。代理类中被调用是在原方法调用时,可是在调用原方法时我们并不会传入Method类型的参数。代理方法和目标方法的参数时一致的,不可能代理中多传入其他参数。因此,代理逻辑只能是在生成代理的类的过程中确定好,即所需要的的一切,必须通过newProxyInstance(Object
object)中传入,而此方法中又不能传入method,因为即使传入了,在代理源文件中它只是一堆字符串,并没有实际的对象,因此我们传入一个对象,就是我们自己实现InvocationHandler接口的实现类的对象。思考点2:我们自己写的InvocationHandler的实现类的重写的方法里,包含了代理逻辑和目标逻辑,如何调用目标逻辑呢?就必须有目标对象和目标方法,即MyInvocationHandler类初始化时必须串入要代理的类的对象,而且invoke()方法必须能拿到目标方法名。怎么拿到呢?
思考点3:代理方法中最终调用MyInvocationHandler重写的invoke(Object proxy, Method
method, Object[] args)方法,则需要传入这么多参数,那说明代理类中也必须能拿到这些参数呀?怎么或许到呢?
按理来说,目标对象proxy是不需要传入的呢?
下面就带着这三个问题,看看JDK动态代理时如何实现的上面的问题的。下面我们称代理类为$Proxy,看下面代码,它是代理某个类生成的源文件
package com.google;
import com.dao.Dao;
import com.luban.dao.CoustomInvocationHandler;
import java.lang.Exception;
import java.lang.reflect.Method;
public class $Proxy implements Dao {
private InvocationHandler h;
// InvocationHandler是我们手写的InvocationHandler的实现类
public $Proxy (InvocationHandler h){
this.h =h;
}
public void query()throws Exception {
// 此method对象是生成本类源码的代码中根据接口的class对象获得的,方法的参数也是如此
Method method = Class.forName("com.dao.Dao").getDeclaredMethod("query");
// 调用这的invoke,invoke里面包含代理逻辑和目标逻辑。jdk动态代理中是需要传入目标对象,方法,和方法参数的。后两者都能解决了,那么
return (void)h.invoke(method);
}
}
下面查看我们自己的InvocationHandler的实现类:
package com.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理逻辑");
return method.invoke(target,args);
}
}
jdk动态代理为什么不使用继承?
因为jdk动态代理的代理对象继承了一个Proxy类,而java是单继承语言,所以代理没有使用继承。jdk动态代理底层,通过接口反射获取到字节码,然后通过native的方法将字节码转换成对象。
cglib
asm是操作字节码的开源框架。cglib就是基于继承,使用asm技术实现。
clazz.newInstance()调用的是默认的构造方法。