`
扫地僧
  • 浏览: 29252 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

一步一步想Java动态代理

阅读更多

版本一

试着只去看API,根据自己的理解,写出了Java动态代理的第一个版本

publicclass DynamicProxyTest

{

    publicstaticinterface IWorker {

       public String work();

    }

   

    publicstaticclass Worker implements IWorker{

        public String work() {

           System.out.println("working...");

           return"hello, work";

        }

    }

   

    publicstaticclass Invocator implements InvocationHandler {

       @Override

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

              throws Throwable {

           System.out.println("before work...");

           String s = (String)method.invoke(proxy);

           System.out.println("after work...");

           returns;

       }

    }

    publicstaticvoid main(String[] args) {

       InvocationHandler invocator = new Invocator();

       IWorker proxyWorker = (IWorker)Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{IWorker.class}, invocator);

       System.out.println(proxyWorker.work());

    }

}

在不知道具体怎么使用动态代理的情况下,主要围绕Proxy.newProxyInstance这个方法猜测用法的。这个方法接收三个参数,如下:

ClassLoader loader:这个好办,把当前类加载器传入即可。

Class<?>[] interfaces:生成的代理类有哪些方法,是通过传入哪些接口类决定的(这也表明了java动态代理是基于接口的);这个参数也没有疑问,直接把我们要代理的接口传入即可:new Class[]{IWorker.class}

InvocationHandler h:实际上在实现InvocationHandler的时候不太清楚怎么实现,主要是不清楚接口中的Object proxy代表什么,于是写成了下面的样子;直接new了一个该实现类当参数传入了。

publicstaticclass Invocator implements InvocationHandler {

       @Override

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

              throws Throwable {

           System.out.println("before work...");

           String s = (String)method.invoke(proxy);

           System.out.println("after work...");

           returns;

       }

    }

不出所料,挂掉了,而且错误信息根本停不下来啊。

想了一下,最有可能出问题的地方是:不太清楚InvocationHandler接口中接口方法invoke(Object proxy, Method method, Object[] args)中,参数proxy究竟是一个什么对象呢?

Proxy.newProxyInstance的源代码(SUN JDK:

第一步: 

/*

         * Look up or generate the designated proxy class.

         */

        Class<?> cl = getProxyClass0(loader, interfaces);

生成代理类的字节码,具体怎么生成的先不管。

第二步:

/** parameter types of a proxy class constructor */

privatefinalstatic Class[] constructorParams =

        { InvocationHandler.class };

final Constructor<?> cons = cl.getConstructor(constructorParams);

获取代理类的构造函数类(构造函数在JVM中使用Constructor类表示)。

第三部:

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

构造函数+构造函数参数生成代理类实例,一个我们需要的代理类就生成的。

现在关于生成的代理类,有以下几点可以确定:

1、代理类以一个InvocationHander实例为参数,该实例是通过Proxy.newProxyInstance传入的。

2、代理类具有它所代理的接口的所有方法,比如,Iworker的代理类,就一定会有一个work()方法,因为我们将来还要通过代理类调用work()方法的。

所以,IWorker生成的代理类大体长这样:

/**

     * 模仿JDK动态生成的代理类

     * JavaIWorker生成的代理类,应该类似我们这里实现的该类。

     * 只不过JDK使用字节码技术动态生成该类,因此灵活性更高。

     * @author Grucee

     */

    publicstaticclass ImitationProxy implements IWorker{

       private InvocationHandler handler;

       public ImitationProxy(InvocationHandler handler) {

           this.handler = handler;

       }

      

       @Override

       public String work() {

           returnnull;

       }

      

    }

那么它的work方法又是怎么实现呢,结合InvocationHandler的接口,该方法应该是知己调用InvocationHandlerinvoke方法:

public String work() {

           return handler.invoke(this, method, args);

       }

上面的代码是编译不通过的,因为我们看似无法获取到methodargs参数。其实不然,代理类怎么知道自己是有哪些方法要实现的,肯定是通过Proxy.newProxyInstance第二个参数(接口类数组),然后通过Class:getDeclaredMethods()获取到有哪些方法要实现,所以它在生成代理类的work方法的时候,肯定就知道它是Iworker.class. getDeclaredMethods[]中的work方法了;当然也就知道这个方法有哪些参数了。

至此,我们终于搞清楚了InvocationHandler:invoke(Object proxy, Method method, Object[] args)中,参数proxy代表什么?它代码的是JDK动态生成的代理类的一个实例。

再思考一个问题,动态生成的代理类会干活么?不会,只有我们自己的IWorker实现类Worker才知道work方法要做什么,所以我们必须要有一个Worker实例才能真正完成工作。因此有了下面的版本二。

版本二

publicclass DynamicProxyTest

{

    publicstaticinterface IWorker {

       public String work();

    }

   

    publicstaticclass Worker implements IWorker{

        public String work() {

           System.out.println("working...");

           return"hello, work";

        }

    }

   

   

    publicstaticclass Invocator implements InvocationHandler {

        private IWorker worker;

        public Invocator(IWorker worker) {

           this.worker = worker;

        }

       

       @Override

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

              throws Throwable {

           System.out.println("before work...");

           String s = (String)method.invoke(worker);

           System.out.println("after work...");

           returns;

       }

    }

    publicstaticvoid main(String[] args) {

       IWorker worker = new Worker();

       InvocationHandler invocator = new Invocator(worker);

       IWorker proxyWorker = (IWorker)Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{IWorker.class}, invocator);

       System.out.println(proxyWorker.work());

    }

}

我们让InvocationHandler持有一个Worker实例,有实例、又有实例的方法,通过反射就可以完成具体的工作了。并且它可以在做具体工作前、后做任意的处理。

版本三

从上面版本二可以看出,要获取一个接口的代理类,我们要提供两个参数:接口类和实现类的一个实例。

如果接口类和实现类的包名再加一些约束条件,即通过接口类可以知道实现类的约束条件,那样我们就可以传入一个接口类,就可以返回一个代理类了。比如最简单的约束条件:接口类和实现类在同一包名下,接口类以IXXX命名,实现类以XXXImpl命名。

publicclass DynamicProxyTest

{

    publicstaticinterface IWorker {

       public String work();

    }

   

    publicstaticclass IWorkerImpl implements IWorker{

        public String work() {

           System.out.println("working...");

           return"hello, work";

        }

    }

   

   

    publicstaticclass Invocator implements InvocationHandler {

        private Object o;

        private Invocator(Object o) {

           this.o = o;

        }

       

       @Override

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

              throws Throwable {

           System.out.println("before work...");

           String s = (String)method.invoke(o);

           System.out.println("after work...");

           returns;

       }

    }

   

    publicstatic Object getProxy(Class<?> iface) throws Exception {

        String ifaceName = iface.getName();

        String[] ifaceSplitted = ifaceName.split("[.]");

       

        String packageName = "";

        intlastSplit = ifaceName.lastIndexOf(".");

        if (lastSplit != -1) {

           packageName = ifaceName.substring(0, lastSplit);

        }

       

        String className = ifaceSplitted[ifaceSplitted.length - 1] + "Impl";

        if (!packageName.equals("")) {

           className = packageName + "."className;

        }

       

        //实例化实现类

        Object o = Class.forName(className).newInstance();

        InvocationHandler invocator = new Invocator(o);

        return Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{iface}, invocator);

    }

   

    publicstaticvoid main(String[] args) throws Exception {

       IWorker worker = (IWorker)getProxy(IWorker.class);

       worker.work();

    }

   

    /**

     * 模仿JDK动态生成的代理类

     * JavaIWorker生成的代理类,应该类似我们这里实现的该类。

     * 只不过JDK使用字节码技术动态生成该类,因此灵活性更高。

     * @author Grucee

     */

    publicstaticclass ImitationProxy implements IWorker{

       private InvocationHandler handler;

      

       public ImitationProxy(InvocationHandler handler) {

           this.handler = handler;

       }

      

       @Override

       public String work() {

           /**

            * Object proxy:这个好办,传入this

            * Method method:代理类怎么知道自己是有哪些方法要实现的,肯定是通过

            * Proxy.newProxyInstance第二个参数(接口类数组),然后通过

            * Class:getDeclaredMethods()获取到有哪些方法要实现,所以它在生成代理类的work方法

            * 的时候,肯定是知道Method是哪个了。

            * Object[] args:方法的参数

            */

           //return handler.invoke(this, method, args);

           return"";

       }

      

    }

}

 

 

分享到:
评论

相关推荐

    java开源包4

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包11

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包6

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包9

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    JAVA上百实例源码以及开源项目

     Java局域网通信——飞鸽传书源代码,大家都知道VB版、VC版还有Delphi版的飞鸽传书软件,但是Java版的确实不多,因此这个Java文件传输实例不可错过,Java网络编程技能的提升很有帮助。 Java聊天程序,包括服务端和...

    java开源包101

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包5

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    JAVA上百实例源码以及开源项目源代码

    Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来...

    java开源包8

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包10

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包3

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包1

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包2

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java开源包7

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    Java资源包01

    JCarder 是一个用来查找多线程应用程序中一些潜在的死锁,通过对 Java 字节码的动态分析来完成死锁分析。 Java的Flash解析、生成器 jActionScript jActionScript 是一个使用了 JavaSWF2 的 Flash 解析器和生成器。...

    java图书销售管理系统源码下载

    相关工具及技术:MVC设计模式、动态代理模式 项目描述:本系统的设计目的是为了满足消费者只要通过互联网就可以足不出户的购买自己喜欢的图书,改变传统商业交易,在互联网上进行交易,实现网上购买图书。为了实现...

    ChatGPT的Java客户端.rar

    今天推荐的这个项目是「ChatGPT-Desktop」,基于 tauri + vue3 开发的 ChatGPT 跨平台客户端,快捷键快速唤醒窗口,问答快人一步。 ChatGPT-Desktop 的优势 使用 tauri 构建项目,使项目包更加小巧精简,资源占用...

    ajax调用java实例源码-WE-CRM:这是每个FHNW网络工程讲座中学生一步一步阐述的参考项目

    ajax调用java实例源码客户关系管理 这是学生在每场 FHNW 网络工程讲座中逐步阐述的参考项目。 分析 设想 WE-CRM(Web Engineering Customer-Relationship-Management)是最小的轻量级演示工具,允许代理管理他们的...

Global site tag (gtag.js) - Google Analytics