aop:面向切面编程。
我的理解就是:对于多个实例对象,把它们码放在一起,在一个或多个特定位置切上一刀,在这个切面的位置上做一些统一的操作。
aop最常用的场景就是日志。
一般来说我们希望在某些函数调用前后记录日志。最容易想到的办法如下:
每次在调用函数的前后加上日志信息。
但如果要对所有对象调用say()函数进行日志,照这样的方法就会出现如下情况
这就产生了面向切面的需求,也就是:我要对所有对象在say函数上切一刀,在这个切面上做一些日志。
现利用java的动态代理方法来实现对函数调用进行代理,可以在代理函数调用中对实际的调用进行日志。
假设有一个接口声明了函数say:
有两个类分别实现了该接口:
我希望这两个类的实例在调用say的前后都能做一下日志。
可以用到java反射中的一些编程接口。
最核心的的方法为,调用
Object java.lang.reflect.
Proxy .newProxyInstance(
ClassLoader loader,
Class <?>[] interfaces,
InvocationHandler h) throws
IllegalArgumentException
它是一个静态方法,需要提供一个类加载器,接口和一个实现InvocationHandler的代理实例,返回一个对应的代理类。
其中第一个参数表示被代理的对象的类的加载器,这里指的就是Hello和Bye的类加载器,第二个参数表示被代理类的接口,这里指的就是ISay。最后一个参数是一个实现InvocationHandler的代理实例,他需要实现 Object invoke(Object proxy, Method method, Object [] arg2) throws Throwable 。
其中proxy就是这个代理本身(可以暂时这样理解,实际有区别)。method就是被代理的对象的当前被调用函数,arg2是这个被调用函数的参数。
我要做的事情是这样的,一般而言产生一个实例对象往往用new来实现,比如ISay h=new Hello(); 而当调用h.say()时,函数会直接被调用。现在我希望劫持这个对象,让的函数调用也可以自定义的进行,此处就是让它在调用say时前后加上日志。
怎么劫持这个对象,就是建立一个该对象的代理,当要调用say方法时委托这个代理对象来完成实际say方法的调用,而代理则可以在实际调用say方法时做一些动作。
实现的方法如下:
这是一个函数调用代理类,首先它实现了invoke方法。其次他有一个delegate成员,类型是Object,即MyProxy可以成为任何对象的函数调用代理,也就是说他可以劫持任何类对象,使得被劫持对象的某个函数被调用时,自动调用invoke方法,你可以在这个方法内自定义被劫持对象的方法会被怎么调用,在此处,当被劫持的任何函数被调用的前后都会打印一些日志。
这是一个工厂类,只有一个静态方法,他接受一个Class类型的参数,返回一个该类的代理对象,即这个类对象已经被劫持,返回的是这个劫持者,但是它的外表看上去没有任何特别,它的类加载器是和原来一样,并且它实现的接口也和原来一样。在getInstance里,首先根据类来建立实例,这就和new的效果一样,然后用MyProxy的实例来做它的函数调用代理,即这个对象的任何函数调用都要经proxy来处理。最后返回用Proxy.newProxyInstance方法产生的代理对象。
测试主函数如下:
可见用ProxyFactory产生的对象和用new产生的对象在调用者看来没有区别,但实际却被劫持了,所以运行结果为:
do before...
Hello!
do after...
do before...
Bye bye!
do after...
但是j2se标准接口中只提供了对实现接口的类的动态代理,所以被代理类必须实现某个接口,否则不能对某个函数进行代理。如果要对普通类进行动态代理,这里不做讨论。