动态代理与AOP----3
InvocationHandler接口
通过反射创建动态代理类对象
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. InvocationHandler接口
1). java.lang.reflect.InvocationHandler接口
(1).InvocationHandler接口基础知识
[1]. InvocationHandler接口中文翻译:方法调用句柄
[2]. InvocationHandler接口所在的位置
InvocationHandler位于java.lang.reflect反射子包中。
[3]. InvocationHandler接口的含义(API)
每一个动态代理类实例(proxyinstance)都有和自身关联的方法调用句柄(invocationhandler)
[4]. InvocationHandler接口的含义
对代理类实例方法调用就会被指派到和这个代理类实例相关联的方法调用句柄中的invoke()去执行。
【简言之】对代理类实例方法调用就等价于(也就是) 对代理类实例相关联的方法调用句柄的invoke方法调用
[5]. InvocationHandler接口的源码
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method,Object[] args)
throws Throwable;
}
【注意】InvocationHandler接口中有且只有一个invoke方法,没有其他的方法。
2). InvocationHandler的invoke方法
(1). 源码声明
public Object invoke(Object proxy, Method method,Object[] args) throws Throwable;
(2). 输入参数
[1]. Object proxy:要调用哪个动态代理类对象的方法
[2]. Method method:要调用某个动态代理类对象的哪个方法
[3]. Object[] args:为要调用的该方法传入哪些实参
(3). 返回值类型 ---- Object类型
2. 通过反射创建动态代理类对象
1). 获取动态代理类和普通类的Class对象
使用反射的基石就是获取对应类的Class类对象
(1). 动态代理类和普通类的区别
[1]. 动态代理类是在程序运行时,由JVM计算在内存中生成。再被类加载器加载,最终获取动态加载类的Class对象。
[2]. 普通类事先通过javac编译成文件系统中字节码文件。然后在JVM运行时由类加载器对文件系统的字节码文件进行加载,最终获得普通类的Class对象。
(2). 动态代理类和普通类的代码上获取Class对象的方式
{1}. 动态代理类获取Class对象 ----- 只能通过Proxy的静态方法getProxyClass()方法
进行的一次关联的对类加载器和要实现的接口数组进行关联
【举例】
Class<?> proxyClass =
Proxy.getProxyClass(ArrayList.class.getClassLoader(), Collection.class);
{2}.动态代理类获取Class对象 【四种方式】
【举例】
【WAY_I】静态方法获取Class对象(包括指定类加载器的重载方法)
Class clazz1 =Class.forName("java.lang.String");
Class clazz2 =Class.forName("java.lang.String", true, ClassLoader.getSystemClassLoader());
【WAY_II】静态属性获取Class对象
Class clazz3 =String.class;
【WAY_III】动态方法获取Class对象
Class clazz4 =new String().getClass();
【WAY_IV】自定义类加载器从特殊的位置获取指定的Class对象
Class clazz5 =new MyClassLoader().loadClass("D:\\BlackHorse\\OuterClass");
2). 通过反射创建动态代理类实例对象
(1). 通过常规的反射方式实例化动态代理类对象
[1]. 示例代码
Class proxyClass =Proxy.getProxyClass(ClassLoader.getSystemClassLoader(),Collection.class);
Constructor con=proxyClass.getConstructor(InvocationHandler.class);
Object proxyInstance =con.newInstance(new InvocationHandler() {
private ArrayList target =new ArrayList();
@Override
public Object invoke(Object proxy, Method method,Object[] args)
throws Throwable {
return method.invoke(target, null);
}
});
System.out.println(proxyInstance.getClass()+"@"+ proxyInstance.hashCode());
[2]. 打印结果:class $Proxy0@1
(2). 动态代理类实例化的简化
为了简化创建动态代理类实例,Proxy将以上通过反射的来获取动态类的实例的步骤进行了简单的封装。Proxy中的静态方法newProxyInstance()就是对上面步骤的封装。
[1]. 方法原型
public static ObjectnewProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throwsIllegalArgumentException;
{1}. 输入参数I: ClassLoaderloader, Class<?>[] interfaces
【说明1】这两个参数用来构建动态代理类的Class对象
【说明2】这两个参数是用来向内部封装的Proxy.getProxyClass()传参。
【注意】这里面的interfaces的参数就不再是getProxyClass(ClassLoader,Class<?>...)后面的可变参数数组类型了。因为这个参数不在位于方法的参数列表的最后位置,所以可变参数数组的使用前提不成立。
{2}. 输入参数II: InvocationHandlerh
【说明1】这个参数用来创建动态代理类的实例对象
【说明2】这个参数是通过动态代理类的构造方法类Constructor对象在调用newInstance()的时候,向这个方法传参。
[2]. 方法源码
public class Proxy implements java.io.Serializable {
//...
//对Proxy的动态代理类,由于只有一个构造方法,并且方法的参数只有一个
//InvocationHandler类型的,所以这里面就直接把这个形参固定住,不在要求用
//户关联形参
private final static Class[] constructorParams = {InvocationHandler.class};
//...
public static Object newProxyInstance(ClassLoaderloader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
if (h == null) {
throw new NullPointerException();
}
Class cl = getProxyClass(loader, interfaces);
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
//...
}
[3]. 应用举例
ClassLoader loader =Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces ={Collection.class};
Object proxyInstance =Proxy.newProxyInstance(loader,interfaces,
new InvocationHandler() {
private ArrayList target =new ArrayList();
@Override
public Object invoke(Object proxy, Method method,Object[] args)
throws Throwable {
return method.invoke(target, args);
}
});
System.out.println(proxyInstance.getClass()+"@"+ proxyInstance.hashCode());
【打印结果】class $Proxy0@1
(3). 通过传统方式使用代理类实例的可行性
[1]. 直接按照Object类型使
【局限性】---- 这种方式局限性比较大 不能直接调用接口类的方法
此时想使用代理类实例proxyInstance直接访问Collection中的方法不能办到。
[2]. 强转成直接父类java.lang.reflect.Proxy----这种方式的局限性也比较大
【局限性】只能使用Object和Proxy的方法,失去了动态代理类的在代理方面的意义。
此时无法使用Collection接口中的方法
[3]. 强转成某一个实现的接口类型----这种办法可行 但是麻烦
【局限性】如果动态代理类对象实现了多个接口,此时想调用不同接口的不同方法,这个时候必须要进行显示强转,使用起来非常麻烦。
[4]. 强转成本类类型$Proxy数字
这种办法是不可行的!!!
因为要想直接在java源代码写出$Proxy0这个类型,要求要有对应的字节码文件存在于文件系统并能被类加载器到指定的位置进行加载。但是我们知道$Proxy0是在JVM运行时,JVM在内存中动态合成的动态代理类的类名。所以在编译的时候,$Proxy0是并不存在的。所以如果在程序中直接进行($Proxy0)强转的话,会编译报错!!!
【结论】通过传统形式操作动态代理类对象总是或多或少存在着局限性
【启发】通过反射形式操作动态代理类对象。
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------