java反射之动态代理学习笔记

来源:互联网 发布:变电话号码的软件 编辑:程序博客网 时间:2024/04/30 09:26

下面是我写的一个小demo,先来看代码

package com.csdn.demo;import com.zhang.jian.zhang;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * Created by zhang on 2016/12/16. * 定义Person接口 */interface Person {    String say(String name);}/***定义Student接口*/interface Student{    String study(String name);}/** * Created by zhang on 2016/12/16. * 定义User类实现Person 和 Student 接口,重写接口方法 */class User implements Person,Student {    public String study(String name) {        System.out.println("User正在学习:"+name);        return name;    }    public String say(String name) {        System.out.println("Person的say方法:" + name);        return name;    }}/** * Created by zhang on 2016/12/16. * 定义 嗯... 我管它叫调用处理器 * 这个主要是用到invoke()方法,作用后面详细介绍 */class MyInvocationHandler implements InvocationHandler {    Object obj = null;/** * Created by zhang on 2016/12/16. * 这个方法起到了两个作用  * 1、给obj赋值,将被代理类赋值给obj * 2、该方法返回一个 实现了被代理类实现的所有接口 的代理类,或者说它返回一个代理类-->被代理类实现的那些 * 接口-->生成的代理类也都实现了(具体后面介绍) */    public Object blind(Object obj) {        this.obj = obj;        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);    }/** * Created by zhang on 2016/12/16. * invoke方法的作用:生成的代理类要重写实现的接口的方法,重写的方法就是来调用这个invoke()方法 */    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("进入了invoke方法了!");        Object returnValue = method.invoke(obj, args);        return returnValue;    }}public class TestProxy {    public static void main(String[] args) {        User user=new User();        MyInvocationHandler handler = new MyInvocationHandler();        Student personProxy = (Student) handler.blind(user);        personProxy.study("Tom");    }}

运行结果:

进入了invoke方法了!User正在学习:TomProcess finished with exit code 0

下面我们来好好分析一下代理类的生成过程

a)调用Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);这条语句生成代理类。
第一个参数:传入被代理类的类类加载器
第二个参数:传入被代理类实现的所有接口
第三个参数:this也就是我们写的那个调用处理器
(如果你对这里有疑虑的话,建议先去看看Class的相关内容:只简单说一下通过Class可以在运行时得到一个类的完整结构)
然后我们点开Proxy的源码文件,发现newProxyInstance方法有如下一条语句
Class<?> cl = getProxyClass(loader, interfaces);
紧接着

 Constructor cons = cl.getConstructor(constructorParams);            return cons.newInstance(new Object[] { h });

不难看出它是通过getProxyClass(loader,interfaces)//loader是传入的类加载器,interfaces是传入的所有接口---
方法返回了一个实现了所有接口的代理类的Class,即这么个东西Class<T>,其中T就是代理类。

b)我们继续去看getProxyClass(loader,interfaces),发现它用到了一个这样的数据类型
static Map<ClassLodar,Map<List<String>,Object>>,通过读代码我们发现这是Proxy用来缓存代理类的,ClassLoader不用说了就是类加载器,List<String>保存的是传入的所有接口的名字,而对应的代理类就保存在Object中。
根据你的传入参数,来判断如果之前已经生成了相应的代理类就直接拿出来返回。
如果没有生成过这样的代理类就调用如下一条语句来生成,并把它缓存起来

//proxyClass就是返回结果,所以Class<T>是通过这个方法生成的 proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); //好吧,这个方法是这样定义的,是的我们看到了native关键字,说明这个方法的实现并不是用java写的 //~那么进行到这里就先结束了(已经懒得再去找了)因为我们已经完全可以进行一下合理的推断了~  private static native Class defineClass0(ClassLoader loader, String name,                                             byte[] b, int off, int len);

c)到这里我们已经大体知道代理类是如何生成的了,只是一些细节还不清楚,但是通过程序的运行结果我们可以来合理的推断。
- 当我们运行上面程序中的代理类personProxy.study("Tom")时,执行的是调用处理器的invoke方法
- 在创建代理类的时候我们传入了一个参数this,也就是调用处理器。生成代理类是这样一条语句return cons.newInstance(new Object[] { h });
- 以上两点,说明生成的代理类有一个构造方法,参数就是一个调用处理器的列表,因此我们也可以推断,代理类的方法的实现就是调用了调用处理器的invoke方法,因此我们只要重写invoke方法就是间接实现了代理类的方法。这就是JDK暴露给我们的一个接口。

到了这里我们是不是对AOP面向切面编程有了初步的认识呢?在调用处理器的invoke方法中如果我们在Object returnValue = method.invoke(obj, args);的上面和下面写点什么东西,是不是就等于在上下两个代码段之间可以动态插入代码了呢?

待更新…

0 0
原创粉丝点击