博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
看完Java的动态代理技术——Pythoner笑了
阅读量:6717 次
发布时间:2019-06-25

本文共 4025 字,大约阅读时间需要 13 分钟。

Java的动态代理常用来包装原始方法调用,用于增强或改写现有方法的逻辑,它在Java技术领域被广为使用,在阿里的Sofa RPC框架序列化中你能看到它的身影,Hibernate的实体类功能增强也是以动态代理的方式解决的,还有Spring吹牛逼的AOP功能也是它搞定的。接下来我们看一个例子,该例子用于对原有的方法调用前后各打印一句话,这也算是对原有类方法的一种增强。

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface IHello {	void say(String s);}// 待加强的目标类class RealHello implements IHello {	@Override	public void say(String s) {		System.out.println("hello " + s);	}}// 增强器class HelloDelegate implements InvocationHandler {	private IHello target;  // 原始对象	public HelloProxy(IHello target) {		this.target = target;	}	@Override	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		System.out.println("before print");		method.invoke(target, args);  // 调用原始对象的方法		System.out.println("after print");		return null;	}}public class DynamicProxy {	public static void main(String[] args) {		IHello hello = enhanceHello(new RealHello());  # 增强原始方法		hello.say("world");	}	public static IHello enhanceHello(IHello target) {		return (IHello) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), new Class
[] { IHello.class }, new HelloDelegate(target)); }}复制代码

输出

before printhello worldafter print复制代码

为了便于理解,我们用图来表示上面的对象的关系。我们调用Proxy.newProxyInstance产生了一个匿名类实例,该实例同样实现了IHello接口,它的作用就是用来替代原生的RealHello实例。这个匿名实例持有HelloDelegate实例的引用,当你对这个匿名实例进行方法调用时,它会将调用逻辑委托给HelloDelegate实例的invoke方法。HelloDelegate实例内部又持有原生RealHello对象的引用,所以用户就可以在invoke方法里实现任意附加逻辑,以及对原生RealHello对象的调用。

上面是jdk自带的动态代理技术,它的缺点是必须定义接口才能实现目标对象的方法增强,甚至想使用abstract class来替代也不行。所以开源市场上冒出了好几个动态代理的库,用于替代原生的jdk动态代理技术,它们不仅仅功能更强大,而且内部使用了字节码增强实现,在性能上还也要比原生jdk高出很多。

javaassist

javaassist是使用最广泛的动态代理开源库。下面我们使用javaassist实现一个无需定义接口就能增强原始方法的例子。

import java.lang.reflect.Method;import javassist.util.proxy.MethodHandler;import javassist.util.proxy.ProxyFactory;class RealHello {	public void say(String s) {		System.out.println("hello " + s);	}}class HelloDelegate
implements MethodHandler { private T target; public HelloDelegate(T target) { this.target = target; } @Override public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable { System.out.println("before print"); method.invoke(target, args); System.out.println("after print"); return null; }}public class DynamicProxy { public static void main(String[] args) { RealHello hello = enhanceHello(new RealHello()); hello.say("world"); } @SuppressWarnings("unchecked") public static
T enhanceHello(T target) { ProxyFactory proxy = new ProxyFactory(); proxy.setSuperclass(RealHello.class); try { HelloDelegate
delegate = new HelloDelegate
(target); // create方法传递了两个空数组 // 分别代表构造器的参数类型数组和构造器的参数实例数组 return (T) proxy.create(new Class
[0], new Object[0], delegate); } catch (Exception e) { e.printStackTrace(); } return null; }}复制代码

输出

before printhello worldafter print复制代码

看起来和原生jdk提供的动态代理区别并不大,达到的效果是一样的。只不过这里要简单了很多,省去了接口类的定义。javaassist的ProxyFactory还提供了方法过滤器,它可以选择性地对特定方法进行增强。

Python

Python是动态语言,对于上面复杂的动态代理技术,它一笑而过。

下面我们来看看Python如果实现所谓的动态代理功能

class Proxy(object):    def __init__(self, target):        self.target = target    def __getattribute__(self, name):        target = object.__getattribute__(self, "target")        attr = object.__getattribute__(target, name)        def newAttr(*args, **kwargs):  # 包装            print "before print"            res = attr(*args, **kwargs)            print "after print"            return res        return newAttrclass RealHello(object):    def prints(self, s):        print 'hello', sif __name__ == '__main__':    t = RealHello()    p = Proxy(t)    p.prints("world")复制代码

输出

before printhello worldafter print复制代码

我们使用了神奇的__getattribute__方法。在Python里面类的属性(方法)都是一个对象,我们先拿到这个类方法对象attr,然后对这个类方法对象进行包装,再返回包装后的新方法对象newAttr。 注意在获取target对象时,不能直接使用self.target,因为self.target会再次调用__getattribute__方法,这样就会导致死循环致堆栈过深曝出异常。取而代之应该使用object.__getattribute__方法来获取对象的属性值。

以上就是Python实现动态代理的方案,读者们,你们是否觉得Python更加简单呢?欢迎大家一起来评论区吵架。

阅读更多精彩文章,关注公众号「码洞」

转载地址:http://ciumo.baihongyu.com/

你可能感兴趣的文章
21、ASP.NET MVC入门到精通——ASP.NET MVC4优化
查看>>
Arm-kernel 内存收集【转】
查看>>
HTML5之Canvas标签简要学习
查看>>
du熊学斐波那契I
查看>>
Silverlight1.1架构图
查看>>
js实现获取值传到input里边
查看>>
【原+转】用CMake代替makefile进行跨平台交叉编译
查看>>
swift3.0:CoreData的使用
查看>>
Silverlight实用窍门系列:3.Silverlight鼠标动态绘制矩形【实例源码下载】
查看>>
postmaster.c 中的 ListenAddresses
查看>>
托付和事件的使用
查看>>
关于Java的转义字符
查看>>
测试管理工具QC使用指南--QC用户
查看>>
使用FDO API连接到各种数据源
查看>>
CentOS 6.5下Redis安装记录
查看>>
Activiti-5.3工作流引擎-源码解析(流程文档解析)
查看>>
python3中的迭代器与生成器
查看>>
开源Flex Air版免费激情美女视频聊天室,免费网络远程视频会议系统((Flex,Fms3联合打造))...
查看>>
在虚拟机中搭建SQLITE环境,并测试其是否安装成
查看>>
jsp页面传递参数是如何与javabean进行关联的
查看>>