一。使用代理模式的场景:对已经开发好的类进行功能增强(添加或修改),比如有一个接口:
package cn.edu.tju.service; public interface Living { void eat(); void drink(); }
以及该接口的一个实现类:
package cn.edu.tju.app; import cn.edu.tju.service.Living; public class ChinesePerson implements Living { @Override public void eat() { System.out.println("开始吃大餐!!!"); } @Override public void drink() { System.out.println("开始喝啤酒!!!"); } }
现在要对ChinesePerson这个类进行增强,在调用eat和drink方法前后打印一些日志。可以创建ChinsePerson的一个代理类(ChinesePersonProxy),该代理类持有一个ChinesePerson对象。
package cn.edu.tju.app; import cn.edu.tju.service.Living; public class ChinesePersonProxy implements Living { private ChinesePerson chinesePerson; public ChinesePersonProxy(ChinesePerson chinesePerson){ this.chinesePerson=chinesePerson; } @Override public void eat() { System.out.println("准备大餐"); chinesePerson.eat(); System.out.println("收拾厨房"); } @Override public void drink() { System.out.println("准备啤酒"); chinesePerson.drink(); System.out.println("收拾酒瓶"); } }
可以写一个主类,调用ChinesePersonProxy这个代理类的方法。
package cn.edu.tju.app; public class ProxyTest01 { public static void main(String[] args) { ChinesePersonProxy personProxy=new ChinesePersonProxy(new ChinesePerson()); personProxy.eat(); personProxy.drink(); } }
运行程序输出如下:
上述方法称为静态代理,代理类中的所有代理方法都是由程序员手动写代码完成。
二。为什么要用动态代理?
假设有一个Living接口的实现类AmericanPerson,代码如下,
package cn.edu.tju.app; import cn.edu.tju.service.Living; public class AmericanPerson implements Living { @Override public void eat() { System.out.println("start eating hamburger"); } @Override public void drink() { System.out.println("start drinking Cola"); } }
现在有一个需求,需要在调用AmericanPerson的eat()方法和drink方法前后都要打印系统当前时间。可以模仿上一小节中的方法来完成AmericanPersonProxy。但是有一个问题:假如Living()接口有10000个方法,而AmericanPerson类实现了Living接口中的10000个方法,要按照静态代理的方法来写AmericanPersonProxy类的话,就需要在该类中写10000个代理方法,显然是不合理的。合理的方法是使用jdk 动态代理。
使用jdk动态代理的AmericanPersonProxy类示例如下:
package cn.edu.tju.app; import cn.edu.tju.service.Living; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Date; public class AmericanPersonProxy implements Living{ AmericanPerson americanPerson=new AmericanPerson(); Living living= (Living) Proxy.newProxyInstance(AmericanPerson.class.getClassLoader(),new Class>[]{Living.class},new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("_______________________________________________________________________"+new Date().toLocaleString()); method.invoke(americanPerson,args); System.out.println("_______________________________________________________________________"+new Date().toLocaleString()); return null; } }); @Override public void eat() { living.eat(); } @Override public void drink() { living.drink(); } }
该代理类持有一个AmericanPerson类的对象: AmericanPerson americanPerson=new AmericanPerson();同时通过Proxy类(jdk的一个类)生成了一个动态类对象:Living living= (Living) Proxy.newProxyInstance(。。。),该代理类的方法eat()和drink()则分别调用这个动态类对象的方法:
living.eat(); living.drink();
写一个测试类,代码如下:
package cn.edu.tju.app; public class ProxyTest02 { public static void main(String[] args) { //System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true"); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); AmericanPersonProxy americanPersonProxy=new AmericanPersonProxy(); americanPersonProxy.eat(); americanPersonProxy.drink(); } }
运行结果如下:
从运行结果可以看出,调用AmericanPersonProxy类对象的eat()和drink()方法时,都调用了该类中定义的动态类对象living构建时所传入的InvocationHandler中的invoke()方法。这是怎么发生的呢?需要看jdk动态生成的类的源码:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import cn.edu.tju.service.Living; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Living { private static Method m1; private static Method m3; private static Method m2; private static Method m4; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void eat() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void drink() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("cn.edu.tju.service.Living").getMethod("eat"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("cn.edu.tju.service.Living").getMethod("drink"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
该类对象通过构造方法,传入InvocationHandler类对象:
public $Proxy0(InvocationHandler var1) throws { super(var1); }
该动态类的父类为Proxy,Proxy类的构造方法如下:
protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; }
而h定义如下:
protected InvocationHandler h;
总结一下就是,Proxy0这个动态类对象在构造时会把传入的InvocationHander对象保存到自己的protected类型的变量h中。
再看动态类里的eat()方法:
public final void eat() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
核心的一句是:
super.h.invoke(this, m3, (Object[])null);
其中super.h为刚才提到的h。那么m3呢?来看静态代码块里的定义
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("cn.edu.tju.service.Living").getMethod("eat"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("cn.edu.tju.service.Living").getMethod("drink"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }
也就是主类中调用eat方法时,最终会调用jdk生成的动态对象的invoke()方法。
invoke(this, Class.forName("cn.edu.tju.service.Living").getMethod("eat"),(Object[])null)
而invoke方法的定义上边已经贴过:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("_______________________________________________________________________"+new Date().toLocaleString()); method.invoke(americanPerson,args); System.out.println("_______________________________________________________________________"+new Date().toLocaleString()); return null; }
代码比较简单,就是反射,不再详述。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)