要让一群人团结起来,需要的不是英明的领导,而是共同的敌人。 收藏本站
登陆 / 注册 搜索

阅读: 4.8K   回复: 4

[# Java] 什么是Java动态代理?

仗剑天涯论坛大牛 2017-10-13 23:57 |显示全部楼层

今生相逢便是缘分,何苦去怨恨,何苦去仇视。

主题破百
        动态代理是设计模式当中代理模式的一种。代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

什么是Java动态代理? cw22-java.jpg

        一、深夜奏对
        
        已经快三更天了, Java帝国的国王还在看着IO大臣的奏章发呆,他有点想不明白, 帝国已经给臣民了提供了这么多的东西,他们为什么还不满意呢? 集合、IO、反射、网络、线程、泛型、JDBC ......在IT界哪一个不都是响当当的硬通货?  有了这些技术,写个Java程序多简单啊, 臣民们为何还整天抗议呢?

        这还是昨天IO大臣的一个奏章,其中说到各个部落要酝酿一场大规模的抗议游行,抗议Java不支持动态性,不能在运行时修改一个类,导致不能用声明的方式来编程。

        国王愤愤地想,我的政策太开明了,这些刁民不知好歹,蹬鼻子上脸,以后要坚决加强东厂西厂锦衣卫镇抚司等纪检法的建设,有意见可以上访, 不能这么胡闹,增加社会不稳定因素。帝国正在和Python, PHP等国家开战,处处都要银子,攘外必先安内啊。

        想到这里,国王立刻命令吕公公宣IO大臣进宫。

        IO大臣半夜里被从热腾腾的的被窝里拽出来,心里老大不情愿, 迷迷糊糊地跟着吕公公进了宫。

        “陛下半夜三更还在为国事操劳,真乃臣等之罪也 !”  IO大臣虽然心里不情愿,但还是毕恭毕敬。

        “爱卿,你说说这是怎么一回事? 什么是Java 不支持动态性? ”国王拿出了奏章。

        IO大臣心里明白了,原来是介个啊。

        “启奏陛下,其实这是刁民们羡慕Python 、Ruby 等语言的动态性,想让我们Java 也支持,他们最想要的一个功能就是能在运行时对类进行修改,这样可以用声明的方式来编程。”

        “你能不能说点朕能听懂的话?” 国王低沉的声音里隐藏着马上就要喷薄而出的怨气,老子想了一晚上都没整明白,你还在这里给我文绉绉的!#j323:

        “是这样”  IO大臣开始调用脑细胞遣词造句,准备用通俗易懂的语言扑灭陛下的怒火。

        “所谓运行时对类进行修改,打个比方来说,我写了一个HelloWorld的类,其中有两个方法:sayHello()和sayHelloToPHP(),陛下请看: ”

什么是Java动态代理? 1-代码.png

        “这是帝国三岁小孩都能明白的代码,说重点!”

        “然后这个类运行起来了,刁民们希望在运行的时候可以修改这类, 譬如加一个新方法sayHelloToPython(), 或者对现在的sayHello()方法里加一点新东西, 甚至把sayHelloToPHP()这个方法删除!”

什么是Java动态代理? 2-代码.png

        “这些刁民太过分了, 难道他们不能写个新的类来做这件事吗?”#j338:

        “陛下圣明, 臣也觉得可以新写一个类比如HelloWorldNew来做这件事情,重新编译一下不就行了吗?  可是他们说的是在运行时修改,是运行时,运行时,运行时,重要的事情说三遍,不是编译时。”

        “运行时? 一个类一旦装入到方法区还怎么修改 ”  国王还是很了解JVM这一套。 “你知道他们为什么有这个要求吗?”

        “他们说了想用声明的方式来编程.....”  IO大臣意识到大事不好。

        “什么是声明的方式”  国王穷追不舍。

        “这个臣还不太清楚......”

        “快去彻查,限你三天回话。”

        “遵旨”。

#f465:

        二、明察暗访
        
        IO大臣冷汗都出来了, 他睡意全无,赶紧召集家丁幕僚准备上山下乡、明察暗访,限他们两天把这个“以声明的方式编程”搞清楚。

        两天内不断有快马回报,各种各样的信息如雪片般飞来。  IO大臣又花了一天时间整理,终于明白了这个“以声明的方式编程”。

        原来这帮刁民犯懒,写完了代码以后有这样的需求:
        
        在某些函数调用前后加上日志记录
        给某些函数加上事务的支持
        给某些函数加上权限控制
        ......

这些需求挺通用的,如果在每个函数中都实现一遍,那重复代码就太多了。 更要命的是有时候代码是别人写的,你只有class 文件,怎么修改? 怎么加上这些功能?

        所以“刁民”们就想了一个损招,他们想在XML文件或者什么地方声明一下, 比如对于添加日志的需求吧, 声明的大意如下:

        对于com.coderising这个package下所有以add开头的方法,在执行之前都要调用Logger.startLog()方法, 在执行之后都要调用Logger.endLog()方法。

        对于增加事务支持的需求,声明的大意如下:

        对于所有以DAO结尾的类,所有的方法执行之前都要调用TransactionManager.begin(),执行之后都要调用TransactionManager.commit(), 如果抛出异常的话调用TransactionManager.rollback()。

        他们已经充分发挥了自己的那点儿小聪明,号称是开发了一个叫AOP的东西,能够读取这个XML中的声明, 并且能够找到那些需要插入日志的类和方法, 接下来就需要修改这些方法了。 但是Java帝国不允许修改一个已经被加载或者正在运行的类, 于是他们就不干了,就要抗议、就要游行,就要暴动, 真是可恶。

        IO大臣决定向国王做一次汇报,看看国王的反应。

#f465:

        三、Java 动态代理
        
        国王不愧是国王, IO大臣稍微一解释, 就明白怎么回事了。

        “爱卿,你觉得该怎么办? ”  皮球又被踢到了IO大臣那里。

        “臣觉得不能让这些刁民突破帝国的底线, 我们的class在运行时是不能被修改的,如果也像Python,Ruby 那样在运行时可以肆意修改,那就太混乱了!”  IO大臣小心翼翼地揣摩圣意。

        “言之有理, 爱卿有何办法? ”

        “臣想到了一个办法,虽然不能修改现有的类,但是可以在运行时动态的创建新的类啊,比如有个类HelloWorld:

什么是Java动态代理? 3-代码.png

        “这么简单的类,怎么还得实现一个接口呢? ” 国王问道。

        “臣想给这些刁民们增加一点点障碍, 你不是想让我动态地创建新的类吗?你必须得有接口才行啊” IO大臣又得意又阴险地笑了。

        国王脸上也露出了一丝不易觉察的微笑。#j327:

        “现在他们的问题是要在sayHello()方法中调用Logger.startLog(), Logger.endLog()添加上日志, 但是这个sayHello()方法又不能修改了!”

什么是Java动态代理? 4-代码.png

        “所以臣想了想, 可以动态地生成一个新类,让这个类作为HelloWorld的代理去做事情(加上日志功能), 陛下请看,这个HelloWorld代理也实现了IHelloWorld接口。 所以在调用方看来,都是IHelloWorld接口, 并不会意识到其实底层其实已经沧海沧田了。”

什么是Java动态代理? 5-代码.png

        “朕能明白你这个绿色的HelloWorld代理,但是你这个类怎么可能知道把Logger的方法加到什么地方呢?” 国王一下子看出了关键。 

        “陛下天资聪慧,臣拜服,‘刁民’们需要写一个类来告诉我们具体把Logger的代码加到什么地方, 这个类必须实现帝国定义的InvocationHandler接口,该接口中有个叫做invoke的方法就是他们写扩展代码的地方。  比如这个LoggerHandler: ”

什么是Java动态代理? 6-代码.jpg

        “ 看起来有些让朕不舒服,不过朕大概明白了, 无非就是在调用真正的方法之前先调用Logger.startLog(), 在调用之后在调用Logger.end(), 这就是对方法进行拦截了,对不对?”

        “正是如此! 其实这个LoggerHandler 充当了一个中间层, 我们自动化生成的类$HelloWorld100会调用它,把sayHello这样的方法调用传递给他 (上图中的method变量),于是sayHello()方法就被添加上了Logger的startLog()和endLog()方法”

什么是Java动态代理? 7-代码.png

        “此外,臣想提醒陛下的是,这个Handler不仅仅能作用于IHelloWorld 这个接口和 HelloWorld这个类,陛下请看,那个target 是个Object, 这就意味着任何类的实例都可以, 当然我们会要求这些类必须得实现接口。  臣民们使用LoggerHandler的时候是这样的:”

什么是Java动态代理? 8-代码.jpg

        输出:
        Start Logging
        Hello World
        End Logging

        “如果想对另外一个接口ICalculator和类Calcualtor做代理, 也可以复用这个LoggerHandler的类:”

什么是Java动态代理? 9-代码.jpg

        “折腾了变天,原来魔法是在Proxy.newProxyInstance(....)  这里,就是动态地生成了一个类嘛, 这个类对臣民们来说是动态生成的, 也是看不到源码的。”#j339:

        “圣明无过陛下,我就是在运行时,在内存中生成了一个新的类,这个类在调用sayHello() 或者add()方法的时候, 其实调用的是LoggerHanlder的invoke 方法, 而那个invoke就会拦截真正的方法调用,添加日志功能了! ”

        “爱卿辛苦了,虽然有点绕,但是理解了还是挺简单的。 朕明天就颁发圣旨, 全国推行,对了你打算叫它什么名字? ”

        “既然是在运行时动态的生成类,并且作为一个真实对象的代理来做事情, 那就叫动态代理吧!”

#f465:

        动态代理技术发布了,臣民们得到了暂时的安抚,但是这个动态代理的缺陷就是必须有接口才能工作,帝国的臣民能忍受得了吗?

本文来自微信公众号:码农翻身(有重新排版)
作者:老刘


soarcloud 「龙战于野」 2017-10-24 09:16 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

好文必顶!
清川带长薄 「出类拔萃」 2018-1-21 10:13 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

漏洞与补丁齐飞,蓝屏共死机一色!
凉冬空巷 「龙战于野」 2018-1-21 13:17 来自手机 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

  觉得冥冥中一直在等撸主写这个帖子。。。我以为。。。撸主的帖子写出来。。。就应该是一部传世之作。。。到现在我才发现。。。什么事情都是可以变的。。。惟独不变的是撸主的精彩原创。。。就好像我一直以为自己文笔不错。。。忽然看到撸主帖子。。。才发现我输了。。。因为在我读撸主的帖子的时候。。。我忘记烦恼和所有不开心的事情。。。整个人好象沐浴在4月杭州温暖的春风中。。。
xinshou10086 「初入古黑」 2018-8-10 20:31 |显示全部楼层

这个用户很懒,还没有填写自我介绍呢~

好闻好文,支持支持。。
您需要登录后才可以回帖 登录 | 免费注册  

本版积分规则

关于本站|大事记|小黑屋|古黑论 网站统计

GMT+8, 2020-10-24 11:09 , Processed in 0.032205 second(s), 21 queries , Redis On.

© 2015-2020 GuHei.Net

Powered by Discuz! X3.4

快速回复 返回列表