Java动态代理

来源:互联网 发布:北京淘宝美工培训2ds 编辑:程序博客网 时间:2024/05/16 13:03

1、什么叫动态代理

“所谓运行时对类进行修改,打个比方来说,我写了一个HelloWorld的类,其中有两个方法:sayHello()和sayHelloToPHP(),
这里写图片描述
然后这个类运行起来了,我们希望在运行的时候可以修改这类:

1、加一个新方法sayHelloToPython();
2、现在的sayHello()方法里加一点新东西;
3、把sayHelloToPHP()这个方法删除;

这里写图片描述

2、为什么要用动态代理

因为会有这样的需求:

在某些函数调用前后加上日志记录
给某些函数加上事务的支持
给某些函数加上权限控制 等等

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

3、动态代理怎么实现

由于Java的class在运行时就是不能被修改的,所以Java采取了一个折中的办法:
虽然不能修改现有的类,但是可以在运行时动态的创建新的类啊。让这个类作为HelloWorld的代理去做事情(加上日志功能),这个HelloWorld代理也实现了IHelloWorld接口。 所以在调用方看来,都是IHelloWorld接口, 并不会意识到其实底层已经改变了。
这里写图片描述

“大家能明白这个绿色的HelloWorld代理,但是你这个类怎么可能知道把Logger的方法加到什么地方呢?”
我们需要写一个类来告诉我们具体把Logger的代码加到什么地方, 这个类必须实现帝国定义的InvocationHandler接口,该接口中有个叫做invoke的方法就是他们写扩展代码的地方。 比如这个LoggerHandler:
这里写图片描述

无非就是在调用真正的方法之前先调用Logger.startLog(), 在调用之后在调用Logger.end(), 这就是对方法进行拦截了。
其实这个LoggerHandler 充当了一个中间层, 我们自动化生成的类$HelloWorld100会调用它,把sayHello这样的方法调用传递给他 (上图中的method变量),于是sayHello()方法就被添加上了Logger的startLog()和endLog()方法。
这里写图片描述
这个Handler不仅仅能作用于IHelloWorld 这个接口和 HelloWorld这个类,陛下请看,那个target 是个Object, 这就意味着任何类的实例都可以, 当然我们会要求这些类必须得实现接口。 臣民们使用LoggerHandler的时候是这样的:
这里写图片描述

输出: Start Logging
Hello World
End Logging

如果想对另外一个接口ICalculator和类Calcualtor做代理, 也可以复用这个LoggerHandler的类:
这里写图片描述
折腾了变天,原来魔法是在Proxy.newProxyInstance(….) 这里,就是动态地生成了一个类嘛, 这个类对臣民们来说是动态生成的, 也是看不到源码的。
我就是在运行时,在内存中生成了一个新的类,这个类在调用sayHello() 或者add()方法的时候, 其实调用的是LoggerHanlder的invoke 方法, 而那个invoke就会拦截真正的方法调用,添加日志功能了!

原创粉丝点击