看完Java的动态代理技术——Pythoner笑了

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 {@Overridepublic void say(String s) {System.out.println("hello " + s);}}// 增强器
class HelloDelegate implements InvocationHandler {private IHello target;  // 原始对象public HelloProxy(IHello target) {this.target = target;}@Overridepublic 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 print
hello world
after 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<T> implements MethodHandler {private T target;public HelloDelegate(T target) {this.target = target;}@Overridepublic 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> T enhanceHello(T target) {ProxyFactory proxy = new ProxyFactory();proxy.setSuperclass(RealHello.class);try {HelloDelegate<T> delegate = new HelloDelegate<T>(target);// create方法传递了两个空数组// 分别代表构造器的参数类型数组和构造器的参数实例数组return (T) proxy.create(new Class<?>[0], new Object[0], delegate);} catch (Exception e) {e.printStackTrace();}return null;}}
复制代码

输出

before print
hello world
after print
复制代码

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

Python

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

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

class Proxy(object):def __init__(self, target):self.target = targetdef __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 resreturn newAttrclass RealHello(object):def prints(self, s):print 'hello', sif __name__ == '__main__':t = RealHello()p = Proxy(t)p.prints("world")
复制代码

输出

before print
hello world
after print
复制代码

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

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

精彩文章,关注公众号「码洞」

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/451622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

shell实现从1加到100

#!/bin/bash # test"while do done"PATH/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATHsum0 for i in $(seq 1 100); do let sumi done echo "The sum is > $sum "

面试常考的数据结构Java实现

1、线性表 2、线性链表 3、栈 4、队列 5、串 6、数组 7、广义表 8、树和二叉树 二叉树&#xff1a;每个结点至多只有两棵子树&#xff08;即二叉树中不存在度大于2的结点&#xff09;&#xff0c;并且&#xff0c;二叉树的子树有左右之分&#xff0c;其次序不能任意颠倒。 二叉…

Java5线程并发库之LOCK(锁)CONDITION(条件)实现线程同步通信

为什么80%的码农都做不了架构师&#xff1f;>>> Lock&#xff08;锁&#xff09;&Condition&#xff08;条件&#xff09;实现线程同步通信 接下来介绍&#xff0c;java5线程并发库里面的锁。跟锁有关的类和接口主要是位于java.util.concurrent.locks包。 Lock…

互联网,可预见的未来

我记忆中的1998年代&#xff0c;PC迅猛发展&#xff0c;CPU速度逐年翻番&#xff0c;持续了7年&#xff0c;但下一个7年到现在&#xff0c;基本上没有太大提升&#xff1b;显示器从14英寸CRT发展到2005的21英寸LED&#xff0c;后来也没有继续进化。为什么&#xff1f;当人对计算…

什么时候用GET?什么时候用POST?

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 一、 GET和POST两种方法都是将数据送到服务器&#xff0c;但你该用哪一种呢&#xff1f; HTTP标准包含这两种方法是为了达到不同的目的…

逻辑运算符与逻辑表达式

1 #include <stdio.h>2 3 int main()4 {5 int a0;int b0;6 if(a&&b)//a&&ba的逻辑值为0&#xff0c;则执行else7 {8 printf("a&&b is true\n");9 } 10 else 11 { 12 printf("a&&…

linux/shell相关知识点

阿里Linux Shell脚本面试25个经典问答 Linux运维工程师12道面试题整理 感谢作者分享&#xff01;

20180601]函数与标量子查询2.txt

[20180601]函数与标量子查询2.txt --//昨天看http://www.cnblogs.com/kerrycode/p/9099507.html链接,里面提到: 通俗来将&#xff0c;当使用标量子查询的时候&#xff0c;ORACLE会将子查询结果缓存在哈希表中&#xff0c; 如果后续的记录出现同样的值&#xff0c;优化器通过缓存…

ODP 使用 ArrayBind 时可能会遇到的巨坑 'System.IConvertible' 的解决方法

Unable to cast object of type System.Nullable1[System.Int16][] to type System.IConvertible 一段代码99%不会出错&#xff0c;0.1%会报上边的错&#xff0c;debug费了老鼻子时间&#xff0c;发现此坑很深。异常是 cmd.ExecuteNonQuery() 抛的&#xff0c;实际是 para.Valu…

eclipse快速定位到错误处

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程 以前都是按着滚动条往下拉&#xff0c;找到错误的地方&#xff0c;有时比较多的时候就很麻烦。 其实eclipse是可以直接快速定位的&#x…

C语言中的“”和“”

先说左移,左移就是把一个数的所有位都向左移动若干位,在C中用<<运算符.例如: int i 1; i i << 2; //把i里的值左移2位 也就是说,1的2进制是000...0001(这里1前面0的个数和int的位数有关,32位机器,gcc里有31个0),左移2位之后变成 000...0100,也就是10进制的4,所以…

网站性能优化的三重境界

这篇文章是关于网站性能优化体验的&#xff0c;性能优化是一个复杂的话题&#xff0c;牵涉的东西非常多&#xff0c;我只是按照我的理解列出了性能优化整个过程中需要考虑的种种因素。点到为止&#xff0c;包含的内容以浅显的介绍为主&#xff0c;如果你有见解能告知我那再好不…

Linux使用RSA实现免密登录(原理)

参考文献Linux密钥rsa加密原理和ssh使用密钥实现免密码登录 感谢作者分享&#xff01;

PYTHON 爬虫笔记十一:Scrapy框架的基本使用

Scrapy框架详解及其基本使用 scrapy框架原理 Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。 其可以应用在数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的&#xff0c; 也可…

java设计把两个字符串的值交换 而不使用中间变量

public class Test {public static void main(String[] args) {String s1 "aaa";String s2 "cccx";s1 s1 s2;s2 s1.substring(0, s1.length()-s2.length());s1 s1.substring(s2.length());System.out.println(s1" - "s2);}}

服务器返回值 解释 ajax提交方式 后台数据刷进前端

转载于:https://www.cnblogs.com/liuliang389897172/p/9120715.html

no typehandler found for property XXXX 解决

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. ssm框架下 启动服务报错如题。 2. 原因&#xff1a; 我的情况是&#xff0c;代码中实体属性映射书写和数据库字段名字不一致。 数据…

C++主流预处理,编译和链接过程

在C的程序的编写过程中&#xff0c;基本上都碰到过LNK2005的错误吧&#xff0c;下面就针对这个问题详细分析&#xff1a;首先&#xff0c;预处理阶段&#xff1a;这一过程&#xff0c;主要针对#include和#define进行处理&#xff0c;具体过程如下&#xff1a;对于cpp文件中经常…

shell中sed -i特殊字符

可参考文献&#xff1a; Linux生产环境上&#xff0c;最常用的一套“sed“技巧 看懂shell中的各种语句

Win10远程桌面提示你的凭据不工作的处理方法

需要确保在组策略编辑器&#xff08;WinR 输入 gpedit.msc &#xff09;中计算机配置->Windows设置->安全设置->本地策略->安全选项->右侧的网络访问:本地帐户的共享和安全模型。修改为使用经典模式即可&#xff01;