动态代理与AOP(3)

来源:互联网 发布:ios11.2 软件打不开 编辑:程序博客网 时间:2024/06/08 18:56

InvocationHandler接口

通过反射创建动态代理类对象

1.  InvocationHandler接口

1). java.lang.reflect.InvocationHandler接口

(1).InvocationHandler接口基础知识

[1]. InvocationHandler接口中文翻译:方法调用句柄

[2]. InvocationHandler接口所在的位置

InvocationHandler位于java.lang.reflect反射子包中。

[3]. InvocationHandler接口的含义(API)

每一个动态代理类实例(proxy instance)都有自身关联方法调用句柄(invocationhandler)

[4]. InvocationHandler接口的含义

代理类实例方法调用就会被指派到和这个代理类实例相关联的方法调用句柄中的invoke()去执行

简言之】对代理类实例方法调用就等价于(也就是)代理类实例相关联的方法调用句柄invoke方法调用

[5]. InvocationHandler接口源码

package java.lang.reflect;

public interface InvocationHandler {

    public Object invoke(Object proxy, Methodmethod, Object[] args)

    throws Throwable;

}

注意InvocationHandler接口中有且只有一个invoke方法没有其他的方法

2). InvocationHandler的invoke方法

(1). 源码声明

public Object invoke(Object proxy, Methodmethod, 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对象(包括指定类加载器的重载方法)

Classclazz1 =Class.forName("java.lang.String");

Classclazz2 =Class.forName("java.lang.String",true,ClassLoader.getSystemClassLoader());

WAY_II静态属性获取Class对象

Classclazz3 =String.class;

WAY_III动态方法获取Class对象

Classclazz4 =newString().getClass();

WAY_IV自定义类加载器特殊的位置获取指定的Class对象

Classclazz5 =newMyClassLoader().loadClass("D:\\BlackHorse\\OuterClass");

2). 通过反射创建动态代理类实例对象

(1). 通过常规的反射方式实例化动态代理类对象

[1]. 示例代码

ClassproxyClass =Proxy.getProxyClass(ClassLoader.getSystemClassLoader(),Collection.class);

 

Constructorcon =proxyClass.getConstructor(InvocationHandler.class);

 

ObjectproxyInstance =con.newInstance(new InvocationHandler() {

    private ArrayListtarget=newArrayList();

    @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)throws IllegalArgumentException;

{1}. 输入参数I:ClassLoader loader, Class<?>[] interfaces

说明1】这两个参数用来构建动态代理类的Class对象

说明2】这两个参数是用来向内部封装的Proxy.getProxyClass()传参

注意】这里面的interfaces的参数就不再是getProxyClass(ClassLoader,Class<?>...)后面的可变参数数组类型了。因为这个参数不在位于方法的参数列表的最后位置,所以可变参数数组的使用前提不成立

{2}. 输入参数II:InvocationHandler h

说明1】这个参数用来创建动态代理类的实例对象

说明2】这个参数是通过动态代理类的构造方法类Constructor对象在调用newInstance()的时候,向这个方法传参

[2]. 方法源码

public class Proxy implements java.io.Serializable {

  //...

  //Proxy的动态代理类,由于只有一个构造方法,并且方法的参数只有一个

//InvocationHandler类型的,所以这里面就直接把这个形参固定住,不在要求用

//户关联形参

privatefinalstaticClass[]constructorParams = {InvocationHandler.class};

//...

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException {

  if (h == null) {

     throw newNullPointerException();

  }

  Classcl = getProxyClass(loader, interfaces);

  try {

     Constructorcons = cl.getConstructor(constructorParams);

     return (Object)cons.newInstance(new Object[] { h });

  } catch(NoSuchMethodException e) {

     throw newInternalError(e.toString());

  } catch(IllegalAccessException e) {

     throw newInternalError(e.toString());

  } catch(InstantiationException e) {

     throw newInternalError(e.toString());

  } catch(InvocationTargetException e) {

     throw newInternalError(e.toString());

  }

}

//...

}

[3]. 应用举例

ClassLoaderloader =Thread.currentThread().getContextClassLoader();

Class<?>[]interfaces ={Collection.class};

ObjectproxyInstance =Proxy.newProxyInstance(loader, interfaces,

new InvocationHandler() {

    private ArrayList target =newArrayList();

    @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)强转的话,会编译报错!!!

结论】通过传统形式操作动态代理类对象总是或多或少存在着局限性

启发通过反射形式操作动态代理类对象