zoukankan      html  css  js  c++  java
  • java动态代理的相关类及使用

      之前看了好几遍java教程关于动态代理的介绍,老觉得理解不够深入,所以今天特意参考API并结合实例,记录自己学习动态代理的心得。

      所谓动态代理,我理解是在程序运行过程中动态创建接口的实例,这里的创建实例并不是直接使用new 构造器的方法实现,而是通过动态代理类代理生成。动态代理类是一个实现了一系列(一个或多个)接口的类。以此相关的还有两个概念:

    1. 代理接口:那些被动态代理类实现的接口;
    2. 代理实例:动态代理类的实例。

      每一个动态代理类都有一个相关联的 invocation handler (额,不知道该怎么翻译)对象,该对象实现了 InvocationHandler接口。 invocation handler 有什么用呢,搁置着先,看看简单的动态代理是如何实现的。

      java提供的Proxy(java.lang.reflect包下)类带有创建动态代理类或者代理类实例的静态方法,动态代理可以有以下两个实现(假如代理接口为ProxyInterface):

    方法一:

    //获得实现InvocationHandler接口的类的实例
    InvocationHandler handler = new MyInvocationHandler(...);
    //创建动态代理类proxyClass,代理的接口为Foo
    Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class } );
    //通过获取动态代理类的构造器,在使用构造器来创建对象,该对象就是实现了接口Foo的实例
    Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler });

    方法二:

    //通过Proxy的newProxyInstance方法直接生成代理实例
    Foo f = (Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),
                new Class[] { Foo.class },
                handler);

      由上可知,要实现代理,必须具备三个要素:1.实现了InvocationHandler接口的类的对象;2.代理接口的class loader;3.代理接口的Class对象的数组;

      之前提到过,每一个动态代理类都有一个与之关联的InvocationHandler接口的类的对象,该对象的作用是什么呢?让我们继续完善以上代码,创建一个实现InvocationHandler接口的类,并且在动态代理类生成对象后,执行对象的方法,完整代码如下:

    //代理接口的代码
    package com.lauyu;
    
    public interface ProxyInterface
    {
        public void func();
    }
    
    //动态代理类的代码
    package com.lauyu;
    
    import java.lang.reflect.*;
    
    //实现InvocationHandler接口的类
    class MyInvocationHandler implements InvocationHandler
    {
        //重写InvocationHandler的invoke方法
        public Object invoke(Object proxy, Method method, Object[] args)
        {
            System.out.println("---正在执行的方法:" + method);
            return null;
        }
    }
    
    public class DynamicProxyTest 
    {
    
        public static void main(String[] args) 
        {
            //创建hander实例
            MyInvocationHandler myHandler = new MyInvocationHandler();
            
            //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例
            ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), 
                                new Class[] { ProxyInterface.class }, 
                                myHandler);
            
            //执行代理实例的方法
            f.func();
        }
    }

      可以看到如下输出:

    ---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func()

      程序的输出正是MyInvocationHandler类的invoke方法的方法体语句,也就是说,代理实例的调用自己的方法时,调用动作会被传递到与代理实例相关联的Hander对象的invoke方法,实际调用的是invoke方法。实际上,可以在invoke方法体内执行代理实例的方法,因为invoke方法的形参提供了执行代理实例的方法的全部信息。下面翻译一下invoke方法的形参的API解释:

    完整方法签名是:Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    • proxy-有方法调用动作的代理实例
    • method-Method类实例(反射相关的类),代表代理实例正在调用的接口的方法
    • args-代理实例正在调用的接口的方法的参数,类型为数组

      只要我们在invoke添加语句:method.invoke(proxy, args),就能调用代理实例的调用的方法,但是这样又会触发handler调用invoke方法,就会导致死循环,何况这里并没有实现代理接口的方法,所以调用意义不大。通常,都是在MyInvocationHandler类的invoke方法体内调用实现了代理接口的类的方法(注意这里实现代理接口的类不是代理实例)。继续完善以上代码,创建一个实现ProxyInterface接口的类,并将实现类的对象传入MyInvocationHandler。

    package com.lauyu;
    
    import java.lang.reflect.*;
    
    //接口实现类
    class ProxyInterfaceInstance implements ProxyInterface
    {
        public void func()
        {
            System.out.println("---正在执行ProxyInterfaceInstance方法func");
        }
    }
    
    //实现InvocationHandler接口的类
    class MyInvocationHandler implements InvocationHandler
    {
        //MyInvocationHandler内部有个代理接口类型的属性
        private ProxyInterface pii;
        
        public MyInvocationHandler(ProxyInterface pii2)
        {
            //这里在构造器中初始化代理接口类型的属性
            this.pii = pii2;
        }
        
        //重写InvocationHandler的invoke方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Exception
        {
            System.out.println("---正在执行的方法:" + method);
            
            //
            method.invoke(pii);
            return null;
        }
    }
    
    public class DynamicProxyTest 
    {
    
        public static void main(String[] args) 
        {
            ProxyInterface pii = new ProxyInterfaceInstance();
            
            //创建hander实例
            MyInvocationHandler myHandler = new MyInvocationHandler(pii);
            
            //通过Proxy.newProxyInstance方法创建代理实例,传入的参数有代理接口的class loader、代理接口数组、hander实例
            ProxyInterface f = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(), 
                            new Class[]{ProxyInterface.class}, 
                            myHandler);
            
            //执行代理实例的方法
            f.func();
        }
    }

      执行结果如下:

    ---正在执行的方法:public abstract void com.lauyu.ProxyInterface.func()
    ---正在执行ProxyInterfaceInstance方法func

      可以看到,当代理实例调用接口的方法时,myHandler对象的invoke方法被执行,且在invoke内部还执行了ProxyInterface接口类的对象的方法func。这样调用func方法有一个很好地灵活性,就是如果想要在func方法执行之前或之后进行某种业务,只要在myHandler对象的invoke方法体内添加即可,而没有必要修改func方法,从而把软件耦合降低,事实上,AOP原理就是这样。

    不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之
  • 相关阅读:
    Java并发(十八):阻塞队列BlockingQueue
    web前端
    python学习总结:目录
    Django -- 5.路由层(URLconf)_基于Django1
    python:linux下字符串转换为JSON
    python:一秒中启动一个下载服务器
    Flask【第十二章】:Flask之Websocket,建立单聊群聊
    Flask【第十一章】:Flask中的CBV以及偏函数+线程安全
    Flask【第十章】:特殊装饰器 @app.before_request 和 @app.after_request 以及@app.errorhandler
    Flask【第九章】:Flask之蓝图
  • 原文地址:https://www.cnblogs.com/lauyu/p/3691955.html
Copyright © 2011-2022 走看看