java静态代理与动态代理

来源:互联网 发布:seo外包服务公司 编辑:程序博客网 时间:2024/05/17 04:10

为什么要使用代理?

如果我要为一个类中的某个方法的执行前和执行后加上某种操作,但又不能修改该类的源码,那我要怎样操作?

我们可以把这个类(被代理类)作为另一个类(代理类)的属性使用,当调用被代理类的某一方法的时候,我就可以为这个方法的前后加上我想要的操作。(类似的方式还有很多)

/* * 被代理类 */public class Eat {//只执行eat方法public void eat(){System.out.println("正在吃饭");}}

/* * 代理类,代替Eat(被代理类)执行eat()方法 */public class EatProxy {private Eat eat;public EatProxy(Eat eat){this.eat = eat;}public void eatProxy(){System.out.println("我要开始吃饭了");//我们想要的吃饭前操作eat.eat();System.out.println("吃饭已经结束了");//我们想要吃饭后的操作}}
/* * 测试类 */public class Test01 {public static void main(String[] args) {Eat eat = new Eat();//得到被代理对象的实例EatProxy eatProxy = new EatProxy(eat);//获得代理对象的实例eatProxy.eatProxy(); //调用代理类的代理方法(就是你想要添加操作的方法所在的方法)}}

上面三个类。我们就完成了一个简单的代理。我们没有修改Eat类的代码,但确实为他添加了我们想要的操作。(静态代理)

但上面的EatProxy代理类太单一了,他只能为Eat类代理,那如果我还想要为其他类代理,那我就要写好多EatProxy这样的代理类,这肯定不是我们想要的。

现在我们重新修改一下EatProxy代理类为ObjectProxy代理类,我们从这个代理类的名字就可以看出,这个代理类可以为任意类型的被代理类进行操作。

/* *  *万能代理类(还不是很完美) *  * */public class ObjectProxy {private Object obj;//要被代理的类型//参数为Object类型,所以你可传你想要传的类型,而不是只能传单一的类型public ObjectProxy(Object obj){this.obj = obj;}public void objProxy(){System.out.println("在该方法前你要做的操作");/* * 你要进行操作的方法,因为是Object类型,所以我们无法调用特定的方法,这里我们可能就要 * 传入你想要传入类型的某一方法 * 比如Eat的eat()方法,Run的run()方法 *  */System.out.println("在该方法后你要做的操作");}}


我们定义一个方法的时候如何确定它的唯一性?就是按这个方式‘只能’找到它,我们肯定要知道这个方法属于哪个类?这个方法的名字叫什么?这个方法的参数?这样我们就可以唯一的确定一个方法了。

所以我们可以修改一下这个类。

public class ObjectProxy {/* * obj:你想要被代理的对象 * method:被代理的方法 * args:方法中的参数,参数类型,数量不确定,所以为Object的数组类型 */public Object objProxy(Object obj,Method method,Object[] args){System.out.println("在该方法前你要做的操作");/* * 你要进行操作的方法,因为是Object类型,所以我们无法调用特定的方法,这里我们可能就要 * 传入你想要传入类型的某一方法 * 比如Eat的eat()方法,Run的run()方法 *  */System.out.println("在该方法后你要做的操作");return null;}}

这样我们“万能”代理的雏形大体就差不多就出来了。我们想一下在一个类运行时要拿到他的方法,肯定要用到反射,反射就是干这种事的,我们也可以从Method类看出来有反射的味道。

我们现在看一下java为我们实现的动态代理(万能代理)和按我们逻辑设计的万能代理有什么异同?

public class MyHandler implements InvocationHandler {//要代理的真实对象private Object realObj;public MyHandler(Object obj) {this.realObj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//这里proxy是我们的代理实例System.out.println("开始我想要的操作");//obj为该方法返回的类型Object obj = method.invoke(realObj, args);System.out.println("结束我的操作");return obj;}}

但这里的proxy我们并没有用到,而且我们也不会显示的调用这个方法,看网上说的是proxy是为了代码的完整性什么的,我也不是很清楚。

现在我们写一个测试类运行输出。

public static void main(String[] args) {Eat eat01 = new EatImpl();MyHandler mh = new MyHandler(eat01);Eat e = (Eat)Proxy.newProxyInstance(eat01.getClass().getClassLoader(),eat01.getClass().getInterfaces() , mh);System.out.println(e.getClass().toString());e.eatThing(25);}
可以看出并没有修改我们想要添加东西的类,但他的行为已经为我们改变了。这我们也看出被代理的类必须实现一个接口不然无法用java的jdk动态代理。我们自己想的和java自己实现的思路是一样的,只是java实现起来比较系统规范严谨一些,所以测试类中的e就是我们动态生成的代理对象,从打印结果也可以看出:class com.sun.proxy.$Proxy0

如果我想对别的类实现在运行时动态的添加行为,只需要改变测试代码和MyHandler,就可以实现了。





原创粉丝点击