美文网首页
Java--动态代理

Java--动态代理

作者: Qi0907 | 来源:发表于2017-06-07 12:02 被阅读0次

    首先先理解一下为什么要使用代理。在日常生活中,代理可以解决业务相关双方直接交流的不便的问题,同时还可以提供比直接交流更多的功能,而在编程领域,代理类一般是做些除原始类核心功能以外的其他功能,比如修改权限等需要专门的代理来实现。代码每个类代表一个主要功能,而不是将所有功能混在一个类中,这样的代码清晰有条理,易于维护,如果要修改权限,不必修改原始类代码,直接修改权限代理类就可以了。这样不需要修改权限的代码可以还用原始类,需要的,就调用代理类,不会影响原来的功能。
    代理类提供一个与原始相同的接口,以便可以在任何时候替代原始。代理类通常在客户端调用传递给原始类之前或之后,执行某个操作,而不是单纯地将调用传递给原始类,同时,代理类可以在执行原始类操作时,附加其他的操作,相当于对原始类进行封装。
    而动态代理的作用是,只要建立一个动态代理类,就可以为多个原始类进行代理,解决了静态代理的问题(一个原始类对应一个静态代理,有多少原始类就有多少静态代理,造成代码琐碎)。
    在java的动态代理机制中,有一个重要的接口 InvocationHandler(Interface)和一个重要的类 Proxy(Class),这两个是实现动态代理所必须用到的。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用

    * @param proxy 所代理的真实对象
         *            the proxy instance on which the method was invoked
         * @param method 所要调用真实对象的某个方法的Method对象
         *            the method invoked on the proxy instance
         * @param args 调用真实对象某个方法时接受的参数
         *            an array of objects containing the parameters passed to the
         *            method, or {@code null} if no arguments are expected.
         *            Primitive types are boxed.
         *
         * @return the result of executing the method. Primitive types are boxed.
         *
         * @throws Throwable
         *             the exception to throw from the invoked method on the proxy.
         *             The exception must match one of the declared exception types
         *             of the invoked method or any unchecked exception type. If not
         *             then an {@code UndeclaredThrowableException} is thrown
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    

    再来看看Proxy类,它的作用是用来动态创建一个代理对象的类,它提供了许多的方法,但是用的最多的就是 newProxyInstance 这个方法

    * @param loader 一个ClassLoader对象,
                                定义了由哪个ClassLoader对象来对生成的代理对象进行加载
         *            the class loader that will define the proxy class
         * @param interfaces 一个Interface对象的数组,
                                    表示的是将要给需要代理的对象提供一组什么接口,
                                    如果提供了一组接口给它,
                                    那么这个代理对象就宣称实现了该接口(多态),
                                    这样就能调用这组接口中的方法了
         *            an array of {@code Class} objects, each one identifying an
         *            interface that will be implemented by the returned proxy
         *            object
         * @param invocationHandler 一个InvocationHandler对象,
                                            表示的是当这个动态代理对象在调用方法的时候,
                                            会关联到哪一个InvocationHandler对象上
         *            the invocation handler that handles the dispatched method
         *            invocations
         * @return a new proxy object that delegates to the handler {@code h}
         * @throws IllegalArgumentException
         *                if any of the interface restrictions are violated
         * @throws NullPointerException
         *                if the interfaces or any of its elements are null
         */
        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                              InvocationHandler invocationHandler)
                throws IllegalArgumentException {
    

    之后看一个demo DynamicProxy20170607来看看动态代理如何使用。
    最后结果:


    Paste_Image.png

    “$ProxyN”是代理类名称,由


    Paste_Image.png
    打印出的,其中 N 是一个逐一递增的阿拉伯数字,代表 Proxy 类第 N 次生成的动态代理类,但不是每次调用 Proxy 的静态方法创建动态代理类都会使得 N 值增加,因为如果对同一组接口(包括接口排列的顺序相同)试图重复创建动态代理类,它会返回先前已经创建好的代理类的类对象,而不会再尝试去创建一个全新的代理类,这样可以节省不必要的代码重复生成,提高了代理类的创建效率。
    通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是InvocationHandler类型,也不是定义的那组接口的类型,而是在运行是动态生成的一个对象。
    之后调用
    Paste_Image.png

    通过代理对象来调用接口中的方法,程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而这个 handler 对象又接受了一个 helloImpl类型的参数


    Paste_Image.png
    表示要代理的就是这个helloImpl真实对象,所以此时就会调用 handler(DynamicProxy) 中的invoke方法去执行,可以看到DynamicProxy中的invoke不仅调用了helloImpl这个真实对象(helloImpl实现了Hello接口),还在前后添加了一些操作,另外通过
    Paste_Image.png
    可以看到具体调用了什么方法,
    结果中before say hello,和after say hello都是添加的操作,从Method:public abstract void DynamicProxy20170607.Hello.helloCat()可以看到,确实是调用的Hello接口helloCat的实现方法。
    Paste_Image.png
    这也就证明了当通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

    相关文章

      网友评论

          本文标题:Java--动态代理

          本文链接:https://www.haomeiwen.com/subject/vdbofxtx.html