动态代理之继承(兄弟)

Posted mostknow

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理之继承(兄弟)相关的知识,希望对你有一定的参考价值。

前言:本文为《Java帝国之动态代理》的姊妹篇, 讲述动态代理的另外一种实现即CGLib的设计过程。

IO大臣绞尽脑汁地在府中设计Java动态代理的时候,他并不知道,在帝国的一个小小的部落,一个年轻的小伙子正在为同样的问题而苦恼。
          

师傅刚刚给小伙子下达了任务:在运行时对一个类进行扩展, 例如有个类叫HelloWorld,要在运行时给他加点日志输出的代码。

 技术分享图片

师傅特别告诉年轻人:“大胖, 你要注意,是在运行时,而不是在编译时! 不能改变原来的代码!”

年轻气盛的张大胖还不太清楚为什么要这么干,不由地问道:“师傅,怎么会有这么稀奇古怪的要求啊?”

师傅说:“你先把这个东西给做出来,我以后自然会给你讲。”

张大胖研究了一段时间后得知,Java帝国严禁在运行时对一个类进行修改,比如HelloWorld 这个类,  一旦被装载进入JVM方法区,就不能改动了,那想在运行时给它的sayHello()方法中加点日志代码该怎么办呢?

 

大胖心想: 那其实只剩下一条路了,动态地生成一个新类 让这个类作为代理HelloWorld去做事情。

 技术分享图片

可是怎么才能动态地生成一个类呢? 张大胖心想: 我曾经看过帝国的《JVM规范》, 一个class文件那简直是太复杂了,难道让我去操作二进制代码去生成类?  那可要了我的小命了! 

 

张大胖拿着草稿图去找师傅:“师傅,我已经有了个初步想法, 可是不知道怎么去动态地生成类啊!”

 

师傅看了一眼说: “你这个类图有问题啊!”

 

“什么问题?”

 

“举个例子,有个函数如下

 技术分享图片

 

现在你把HelloWorld变成了HelloWorldProxy, 而这个HelloWorldProxy又和原来的HelloWorld一毛钱关系都没有, 还能作为参数传递給hello方法吗? ”

张大胖挠挠头,挺不好意思 “奥,那是肯定不行喽,没法利用多态,这样,我可以让这个动态生成的HelloWorldProxy继承自HelloWorld, 师傅你看看这样行不行:”

 技术分享图片

 

“这样好多了, 但是还有一个问题,就是代码的复用性不够。”

 

“什么复用性?”

 

“你想想啊,现在你只有一个类就是HelloWorld , 如果还有很多别的类例如Person, Student, Employee,Teacher ..... 他们相应的方法都要加上日志输出,按照你的办法,就得有无数这样的代码了:

Logger.startLog();

super.XXXX();

Logger.endLog();”

 

“奥,我明白了,就是要想办法复用这一段代码,那我可以再增加一个中间层,就叫做LogInterceptor如何?”

 技术分享图片

 

“孺子可教,你这个名称起得也不错,Interceptor,意味着拦截的意思。这样一来LogInterceptor就可以被其他类给复用了。”

 

师傅, 回到我最初的问题, 怎么在运行时动态地生成Java Class啊? 总不能让我直接写Java字节码吧?”

 

“这个你不用担心,有个叫做ASM的家伙,他已经对底层的Java字节码操作做了封装,你直接调用它就行了”

“好, 让我去看看,这个玩意儿到底怎么样。”

ASM确实挺难的, 虽然对字节码操作做了封装,但是非得理解JVM指令才行,张大胖不得不去学习一下JVM字节码的知识,两个月后,他终于能够使用ASM动态的在内存中创建类了。 

 

又花了两个月,张大胖终于把整个系统开发完成,现在的使用非常简单:

 技术分享图片

 

张大胖把这个东西命名为动态代理 因为所做的所有事情无非就是在运行时为原有的类建立一个代理,增加功能而已。 

 

过了两天, 师傅急匆匆地来找张大胖:“大胖, 我刚刚听说, Java帝国的IO大臣在JDK中加入了一个重要功能,叫做Java 动态代理,你赶紧研究下,看看和咱们做的有什么不同。

 

大胖不敢怠慢,赶紧查看帝国发布的公告文书, 看完以后就放心了:“师傅, 这官方的动态代理有个重大的缺陷,就是必须有接口才能使用,而我们做的动态代理只要有个类就可以了, 我可以动态地生成一个子类。当然如果一个类被标记为final , 无法被继承,那就不行了。”

 技术分享图片

 

“嗯,有点意思” 师傅说道 “这官方动态生成的HelloWorldProxy是HelloWorld的兄弟 而我们动态生成的HelloWorldProxy是HelloWorld的孩子啊!

 

“哈哈,果然是这样。 师傅,你还没给我说这玩意儿到底有啥用处呢”

 

“你没看官方的公告吗?”

 

“官方大话套话连篇,看不懂啊!”

 

“还拿之前的例子来说吧,你现在有很多类,例如Person, Student, Employee,Teacher ... , 每个类都有很多方法, 现在你想给这些方法加上日志输出,该怎么办呢?”

 

张大胖说:“我可以去改动代码, 嗯,这样改动量非常大,并且如果拿不到源码的话,就没办法了。”

 

“对啊,这时候动态代理不就派上用场了? 动态生成代理类PersonProxy, StudentProxy,EmployProxy... 等等, 让它们去继承Person, Student, Employee,   这样代理类就可以增加日志输出代码了。 你甚至可以把要添加日志功能的类和方法写到一个XML文件中去, 然后再写个工具去读取这个XML文件,自动地生成所有代理类,多方便啊。”

 

“奥,原来如此 ,不仅仅是日志,还有事务了, 权限检查了,都可以用这种办法,对吧 ” 张大胖一点就通。

 

“是这样的, 这就是AOP编程了。  对了,既然官方已经把动态代理这个名称给占了, 我们就得改名了,不能叫做动态代理了”

 

“师傅,这个我已经想好了,叫做Code Generation Library 简称CGLib, 体现了技术的本质,就是一个代码生成的工具。”

 

后记CGLIb为了提高性能,还用了一种叫做FastClass的方式来直接调用一个对象的方法,而不是通过反射。   由于涉及的代码太多,本文不再展示,一个具体的调用过程参见下图:

 

 技术分享图片

 

 

 

 

本文出自 码农翻身


以上是关于动态代理之继承(兄弟)的主要内容,如果未能解决你的问题,请参考以下文章

Spring第三天

Java动态代理和cglib动态代理

动态代理的两种方式以及区别

Spring之AOP

动态代理:JDK动态代理和CGLIB代理的区别

动态代理:JDK动态代理和CGLIB代理的区别