Java--动态代理

首先先理解一下为什么要使用代理。在日常生活中,代理可以解决业务相关双方直接交流的不便的问题,同时还可以提供比直接交流更多的功能,而在编程领域,代理类一般是做些除原始类核心功能以外的其他功能,比如修改权限等需要专门的代理来实现。代码每个类代表一个主要功能,而不是将所有功能混在一个类中,这样的代码清晰有条理,易于维护,如果要修改权限,不必修改原始类代码,直接修改权限代理类就可以了。这样不需要修改权限的代码可以还用原始类,需要的,就调用代理类,不会影响原来的功能。
代理类提供一个与原始相同的接口,以便可以在任何时候替代原始。代理类通常在客户端调用传递给原始类之前或之后,执行某个操作,而不是单纯地将调用传递给原始类,同时,代理类可以在执行原始类操作时,附加其他的操作,相当于对原始类进行封装。
而动态代理的作用是,只要建立一个动态代理类,就可以为多个原始类进行代理,解决了静态代理的问题(一个原始类对应一个静态代理,有多少原始类就有多少静态代理,造成代码琐碎)。
在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方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • Java动态代理 引言 最近在看AOP代码,其中利用到了Java动态代理机制来实现AOP织入。所以好好地把Java...
    草捏子阅读 1,534评论 0 18
  • 前言 本文是我在学习代理模式时的一篇笔记,除了对代理模式、静态和动态代理的概念和实现进行了描述外,还有关于动态代理...
  • 之前介绍的反射和注解都是Java中的动态特性,还有即将介绍的动态代理也是Java中的一个动态特性。这些动态特性使得...
    Single_YAM阅读 2,051评论 0 9
  • 基础:class文件简介及加载流程 Java编译器编译好Java文件之后,产生.class 文件在磁盘中。这种cl...
    jiangmo阅读 478评论 0 1
  • 好不容易把书整理好,她趴在桌上,趴着趴着,她闭上了眼睛… 她便是此文的女主人公:蕾,她一直以来都是按照自...
    鱼小雾阅读 233评论 0 0