`
扫地僧
  • 浏览: 29905 次
  • 性别: 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类加载机制与动态代理

    ### JAVA类加载机制与动态代理 #### 一、类加载机制 ##### 1.1 类加载的时机 类加载机制负责将描述类的数据从`.class`文件加载到内存,并进行必要的校验、转换解析和初始化,使之成为可以被Java虚拟机直接使用的...

    JAVA动态代理模式.pdf

    实际开发中,应深入研究每一步的实现细节,理解动态代理类的加载和生成过程,以及如何通过自定义InvocationHandler来控制方法调用的流程。同时,应该考虑如何正确处理方法调用中可能出现的异常情况,保证程序的健壮...

    动态代理及静态代理及ssh整合

    动态代理和静态代理是代理模式的两种主要实现方式,它们在Java编程中尤为常见。 首先,让我们从静态代理开始。静态代理是手动创建的代理类,它通常与被代理类位于同一个包下,并且实现相同的接口。在这个过程中,...

    java代理模式

    在Java中,代理模式有静态代理和动态代理两种实现方式。 首先,我们来详细了解一下静态代理。在静态代理中,我们需要创建一个代理类,这个代理类与目标类实现相同的接口,代理类在调用目标类方法的同时,可以添加...

    java 三大代理模式及其实现

    CGLIB(Code Generation Library)是另一个常用的动态代理库,尤其适用于Java代理目标类没有实现接口的情况。CGLIB通过生成目标类的子类来创建代理对象。 **实现步骤**: 1. 引入CGLIB库。 2. 创建一个实现了`...

    JDK动态代理 spring aop 的原理

    在Java编程领域,JDK动态代理是实现动态创建代理对象的一种技术,它是Java标准库提供的一种强大工具。Spring AOP(面向切面编程)则是一种流行的应用框架,它利用动态代理来实现对业务代码的切面增强,如日志、事务...

    java开源包4

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

    Java中的静态代理最全讲义

    最后一步是使用代理类来控制真实主题类的行为。在主程序中,我们创建真实主题类和代理类的实例,并通过代理类调用真实主题类的方法。 **代码示例:** ```java public class Main { public static void main...

    以muppet为例封装动态代理简化使用1

    在Java中,JDK提供了对动态代理的支持,允许我们在运行时创建符合指定接口的新类。这种新类(代理类)可以拦截对被代理对象的方法调用,并在调用前后执行自定义的操作,如日志记录、性能监控等。\n\n1. 静态代理与...

    简谈jdk动态代理

    4. **生成动态代理对象并使用**:最后一步是使用`Proxy`类创建动态代理对象,并调用其方法。 ```java UserManager userManager = new UserManagerIml(); InvocationHandler ih = new UserShiWuHand(userManager);...

    java reflection in action

    Java反射是Java编程语言中的一个强大特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。...无论是进行元编程、动态代理还是框架开发,理解并掌握Java反射都是提高Java开发能力的重要一步。

    java开源包6

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

    java开源包9

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

    java开源包101

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

    java开源包5

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

    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 解析器和生成器。...

Global site tag (gtag.js) - Google Analytics