动态代理与AOP(4)

来源:互联网 发布:吹笛子软件安卓版 编辑:程序博客网 时间:2024/06/03 21:55

推理动态代理类的内部结构I

InvocationHandler实现类的具体写法

由于动态代理类存在于内存而不是文件系统中,更不可能存在什么源文件。但是如果能对动态代理类的内存字节码对应的源文件代码进行合理的推断来,那么有助于对动态代理类运行原理一个更好的理解

1.  推理动态代理类的内部结构I

1). 推断动态代理类的内部结构1-------从动态代理类的继承父类实现的接口类以及构造考虑

1). 动态代理类的骨架推断

假设这个动态代理类实现的接口是java.util.Collection类名$Proxy0(仅仅Collection中的一些方法)。源代码的骨架如下 ( 初步结构 ):

$Proxy0结构I

importjava.lang.reflect.InvocationHandler;

importjava.lang.reflect.Proxy;

import java.util.ArrayList;

import java.util.Collection;

 

public class $Proxy0 extends Proxyimplements Collection{

 

    private ArrayListtarget=new ArrayList();

   

    protected$Proxy0(InvocationHandler h) {

       super(h);

    }

 

    public int size() {

       return 0;

    }

 

    public boolean isEmpty() {

       return false;

    }

 

    public boolean contains(Objecto) {

       return false;

    }

 

    public Object[] toArray() {

       // TODO Auto-generated method stub

       return null;

    }

}

2). 推断动态代理类的内部结构2-----从动态代理类存在的意义考虑

(1). 通过反射调用代理类实例的方法的示例代码

[1]. 测试代码

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

Constructor con =proxyClass.getConstructor(InvocationHandler.class);

 

Object proxyInstance =con.newInstance(new InvocationHandler() {

    private ArrayListtarget =new ArrayList();

    @Override

    public Object invoke(Objectproxy, Method method, Object[] args)

           throws Throwable {

       return method.invoke(target, args);

    }

});

 

Method methodAdd =proxyClass.getMethod("contains", Object.class);

Object retVal =methodAdd.invoke(proxyInstance, new Integer(124));

 

System.out.println(retVal);

[2]. 打印结果:false

(2). 将反射操作的双关联应用到动态代理类

通过反射来操作代理类实例的时候出现了以下的关联 (以前讲到的反射过程的双关联)

[1]. 关联代理类方法的方法名形式参数

====>封装成Method对象表示

MethodmethodAdd =proxyClass.getMethod("contains", Object.class);

[2]. 关联代理类方法的形式参数所属的对象

====>这一步没有进行封装,直接作为参数传入Method对象的方法中。

Object retVal=methodAdd.invoke(proxyInstance, new Integer(124)); 

[3]. “动态代理”类的操作剖析

{1}. 代理类本意执行的动作:主要是调用目标类对象对应要执行的方法

( 同方法名,同参数列表 ) =====>反射的角度讲:代理类对象和目标类对象对应的Method对象一定是同一个(因为Method对象仅仅是对方法名和参数列表的封装)

{2}. 代理类对象调用方法VS目标类对象调用对应的方法反射格式比对

原来通过反射调用代理类对象的方法目标类对象的方法的格式对比如下:


[4]. 代理的核心动作交叉业务

{1}. 代理类存在的意义回顾

按照代理类存在意义客户端程序调用代理类程序方法代理类程序方法内部就会调用目标类对应的方法

{2}. 代理类核心动作调用目标类对象的对应的同名+同参的方法

由于存在:外部调用代理类对象的方法代理类内部调用目标类对象的方法的对应关系

因此一旦在程序的外面调用调用代理类对象某个方法(无论是通过反射的方式还是通过传统方式),就一定会在代理类对象方法内部按照上面的“目标格式”调用目标对象的方法

{3}. 代理类交叉业务(AOP):要在调用目标类对象同名方法周围添加交叉业务代码

这样代理类内部的所有方法就会出现下面的场面:


【$Proxy0类代码优化

【1】上面红色点划线圈起来的这些代码块执行的内容一样的,可以抽取成为单独的方法放置于动态代理类的内部。

【2】抽取出来的这个方法的方法体重含有几个未有定义的变量,分别是method对象,以及method自身方法需要的invoke()方法的实参列表。因此就可以抽取的方法的参数列表可以设置为Method和Object[] (分别代表该代理类实例的哪个方法要为该代理类实例的该方法传入哪些参数)【目标类对象代理类对象公用同一个Method类对象method同一实参列表

【3】现在假设方法抽取如下:

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

    //交叉业务

    Object retVal =method.invoke(target,args);

    //交叉业务

    return retVal;

}

此时动态代理类$Proxy0类的代码演化成如下的结构

$Proxy0结构II


3). 推断动态代理类的内部结构3-----从动态代理类仅存在与内存中考虑

由于动态代理类字节码并不存在文件系统,但是交叉业务是要根据具体的需求先于JVM运行程序已经设计好。所以必须预先设计好的带有交叉业务invoke方法预先存在另一个独立存在于文件系统的类中,才能在JVM动态生成代理类的时候,为代理类的每一个方法进行对将已经存在的描述交叉业务的invoke方法进行调用。

(1). 对invoke抽取到其他类时候参数列表的修改

[1]. 使invoke方法从$Proxy0类中独立出来

{1}. 在上面推断出来的invoke的方法的声明是public Object invoke(Method method, Object[] args);这个方法本属于$Proxy0这个类,但是现在的目标要提取出这个类到新的类中。现在$Proxy0中的其他非静态方法都对这个invoke方法进行了调用,举例如下:

public boolean contains(Objecto) {

    Object retVal =invoke(this.getClass().getMethod("contains"), Object.class);

    return retVal;

}

{2}. 【注意】两个非静态成员containsinvoke之间进行直接调用,实际上是在被调用前面加上了this。也就是contains方法中真实的是这样调用invoke方法:

this.invoke(this.getClass().getMethod("contains"), Object.class);

所以invoke方法实际上是$Proxy0实例 (this)所调用的。为了在将invoke方法从$Proxy0中独立出去之后,为了仍然不失去这个信息,为这个方法扩充一个参数Object proxy。

{3}. 这样扩充参数之后的invoke的方法如下

public Object invoke(Objectproxy, Method method, Object[] args){

    //交叉业务

    Object retVal =method.invoke(target,args);

    //交叉业务

    return retVal;

}

现在假设这个invoke方法被提取到叫MyInvocationHandler的类中,假设这个类如下:

public classMyInvocationHandler {

    public invoke(Object proxy,Method method, Object...args){

       //交叉业务

       method.invoke(target, args);

       //交叉业务

    }

}

[2]. 为MyInvocationHandler类添加目标类成员

注意MyInvocationHandlerinvoke方法用到的target对象没有定义。这个target本属于$Proxy0类中成员变量。这个成员变量并没有随着invoke方法抽取被抽取出来。由于这个成员变量仅仅$Proxy0invoke调用,并没有被其他的$Proxy方法所调用,因此就可以将这个本属于$Proxy0类的成员变量target直接抽取成MyInvocationHandler类的成员。因此完整的MyInvocationHandler的结构如下:


 (2). InvocationHandler接口的诞生:对invoke方法所在的类进行向上抽取 ----抽取成接口

[1]. MyInvocationHandler类抽取成接口不是抽象类

注意到抽取出来MyInvocationHandler类invoke方法中的交叉业务部分不是固定的,需要根据不同的需求进行确定。因此有必要对这个类再次进行向上抽取。由于这个类仅仅含有一个方法,并且这个方法体不固定并且没有默认的交叉业务,所以就可以抽取成一个接口(而不是抽象类!!!)。

[2]. InvocationHandler接口

Java就将这个对含有交叉业务的invoke方法所在的类共同向上抽取接口取名为java.lang.reflect.InvocationHandler接口。

[3]. InvocationHandler接口的真正含义

[1]. 首先回顾一下这个接口java.lang.reflect.InvocationHandler来历


[2].【结论1】InvocationHandler接口中invoke方法真实含义

InvocationHandler接口中的方法publicObject invoke(Object proxy, Method method, Object...args)参数Objectproxy表示的代理类对象所属的代理类$Proxy中方法Method(以反射的形式表示)中调用原本属于proxy的对象invoke方法。

[3].【结论3】InvocationHandler类名的含义

{1}. handle和invocation的在英语中的含义

handle本身的意思“处理”

invocation本身的意思“调用”

invocationHandler直译:调用处理器-----计算机学术化:(方法)调用句柄

{2}. 通过上面的推理分析,方法调用处理句柄(InvocationHandler) 就是对动态代理类的实例的invoke方法的调用和处理

1.  InvocationHandler实现类的具体写法【总结】

InvocationHandler接口实现类的构成

(1). 定义成员变量

定义具有和动态代理类相同实现接口实现类成员变量作为目标类对象

---------------------不要忘记对代理类代理的目标类对象进行初始化

(2). 实现成员方法invoke()

实现的invoke方法方法体中自定义交叉业务通过Method对象目标类对象的方法进行调用{实现代理类的根本目的调用目标类方法并且定义交叉业务}



原创粉丝点击