谈谈动态代理

动态代理是基于什么原理?


反射机制是Java语言提供的一种基础功能,赋予程序在运行是自省的梦里,通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类的定义,动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都是利用类似机制做到的,比如用来包装RPC调用、面向切面的编程(AOP)。实现动态代理的方式很多,比如JDK自身提供的动态代理,就是主要利用了上面提供的反射机制。还有其他的实现方式,比如利用传说中更高性能的cglib、javassist等。


反射中提供了一种机制,AccessibleObject.setAccessible ,它的子类也大都重写这个方法,这里的所谓的Accessible 可以理解为修改成员的访问属性,public、private、protected.这意味着我们可以在运行是修改成员访问限制!


这个功能应用场景普遍。比如我们在O/R Mapping 框架中,我们为一个Java实体对象,运行时自动生成setter、getter的逻辑,这是加载或者持久化数据很必要的。
但是在Java 9中引入了一个Open的概念,只有当被反射操作的模块和指定的包对反射调用者模块Open,才能使用setAccessible,否则认为是非法操作,但是目前反射使用的比较广泛,根据社区讨论,目前,Java 9仍然保留兼容Java8 的行为,可能未来某个版本会强制开启。


它到底解决什么问题?


首先,它是一种代理机制,如果熟悉涉及模式中的代理模式,我们知道,代理可以看作是对调用目标的一个包装,这样我们队目标代码的调用不是直接发生,而是通过代理完成。其实很多代理模式,我认为可以看作是装饰器模式的使用,通过代理可以让调用者与实现者之前解耦,比如进行RPC调用,框架内部的寻址、序列化、反序列化等,对于调用者往往没有太大意义,通过代理,可以提供更好的方式。


JDK proxy 实现代理方式是需要实现InvocationHandler。然后,为被调用目标构建代理对象,从API设计和实现的角度,这种实现仍然有局限性,因为它是以接口为中心的。相当于添加了一种对于被调用者没有太大意义的限制。我们实例化的是proxy 对象,而不是真正的被调用类型。


如果被调用者没有实现接口,而我们需要用动态代理机制,可以选择cglib方式,比如spring AOP就支持两种模式的动态代理,JDK proxy或者cglib。


JDK Proxy 的优势:


最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。


平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。


代码实现简单。


基于类似 cglib 框架的优势:


有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。


只操作我们关心的类,而不必为其他相关类增加工作量。


高性能。


另外,从性能角度,我想补充几句。记得有人曾经得出结论说 JDK Proxy 比 cglib 或者 Javassist 慢几十倍。坦白说,不去争论具体的 benchmark 细节,在主流 JDK 版本中,JDK Proxy 在典型场景可以提供对等的性能水平,数量级的差距基本上不是广泛存在的。而且,反射机制性能在现代 JDK 中,自身已经得到了极大的改进和优化,同时,JDK 很多功能也不完全是反射,同样使用了 ASM 进行字节码操作。


我们在选型中,性能未必是唯一考量,可靠性、可维护性、编程工作量等往往是更主要的考虑因素,毕竟标准类库和反射编程的门槛要低得多,代码量也是更加可控的,如果我们比较下不同开源项目在动态代理开发上的投入,也能看到这一点。

猜你喜欢

转载自blog.csdn.net/wzbwzh/article/details/80395753