aop原理学习——动态代理

aop:面向切面编程。

我的理解就是:对于多个实例对象,把它们码放在一起,在一个或多个特定位置切上一刀,在这个切面的位置上做一些统一的操作。

aop最常用的场景就是日志。

一般来说我们希望在某些函数调用前后记录日志。最容易想到的办法如下:package com.red.myaop.bean; public class EasyLog { /** * @param args */ public static void main(String[] args) { EasyLog easy = new EasyLog(); System.out.println(“do before…”); easy.say(); System.out.println(“do after…”); } public void say(){ System.out.println(“hello”); } } 

每次在调用函数的前后加上日志信息。

但如果要对所有对象调用say()函数进行日志,照这样的方法就会出现如下情况。。。。。 System.out.println(“do before…”); easy.say(); System.out.println(“do after…”); System.out.println(“do before…”); easy1.say(); System.out.println(“do after…”); System.out.println(“do before…”); easy2.say(); System.out.println(“do after…”); 。。。。。。

这就产生了面向切面的需求,也就是:我要对所有对象在say函数上切一刀,在这个切面上做一些日志。

现利用java的动态代理方法来实现对函数调用进行代理,可以在代理函数调用中对实际的调用进行日志。

假设有一个接口声明了函数say:

package com.red.myaop.bean; public interface ISay { public void say(); }

有两个类分别实现了该接口:

package com.red.myaop.bean; public class Hello implements ISay{ @Override public void say() { System.out.println(“Hello!”); } }

package com.red.myaop.bean; public class Bye implements ISay { @Override public void say() { System.out.println(“Bye bye!”); } }

我希望这两个类的实例在调用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方法时做一些动作。

实现的方法如下:

package com.red.myaop.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyProxy implements InvocationHandler { private Object delegate; public Object getDelegate() { return delegate; } public void setDelegate(Object delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable { System.out.println(“do before…”); Object ret = method.invoke(this.delegate, arg2); System.out.println(“do after…”); return ret; } }

这是一个函数调用代理类,首先它实现了invoke方法。其次他有一个delegate成员,类型是Object,即MyProxy可以成为任何对象的函数调用代理,也就是说他可以劫持任何类对象,使得被劫持对象的某个函数被调用时,自动调用invoke方法,你可以在这个方法内自定义被劫持对象的方法会被怎么调用,在此处,当被劫持的任何函数被调用的前后都会打印一些日志。

package com.red.myaop.proxy; import java.lang.reflect.Proxy; public class ProxyFactory { public static Object getInstance(Class clazz) { MyProxy proxy = new MyProxy(); try { proxy.setDelegate(clazz.newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), proxy); } }

这是一个工厂类,只有一个静态方法,他接受一个Class类型的参数,返回一个该类的代理对象,即这个类对象已经被劫持,返回的是这个劫持者,但是它的外表看上去没有任何特别,它的类加载器是和原来一样,并且它实现的接口也和原来一样。在getInstance里,首先根据类来建立实例,这就和new的效果一样,然后用MyProxy的实例来做它的函数调用代理,即这个对象的任何函数调用都要经proxy来处理。最后返回用Proxy.newProxyInstance方法产生的代理对象。

测试主函数如下:

package com.red.myaop.bean; import com.red.myaop.proxy.ProxyFactory; public class Tester { public static void main(String[] args) { ISay hello = (ISay)ProxyFactory.getInstance(Hello.class); ISay bye= (ISay)ProxyFactory.getInstance(Bye.class); hello.say(); bye.say(); } }

可见用ProxyFactory产生的对象和用new产生的对象在调用者看来没有区别,但实际却被劫持了,所以运行结果为:

do before…
Hello!
do after…
do before…
Bye bye!
do after…

但是j2se标准接口中只提供了对实现接口的类的动态代理,所以被代理类必须实现某个接口,否则不能对某个函数进行代理。如果要对普通类进行动态代理,这里不做讨论。

 

    原文作者:AOP
    原文地址: https://blog.csdn.net/chyroger/article/details/6309425
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞