代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
类图:
模拟情景:
1.有一个Tank类,实现Moveable接口中的move(),方法,现在想要对Tank的move()方法前后做些日志记录的操作,这时就可以使用代理。
public interface Moveable { void move(); }
2.Tank类
import java.util.Random; /** * 被代理对象 * @author dell * */ public class Tank implements Moveable{ @Override public void move() { System.out.println("Tank moving..."); try { Thread.sleep(new Random().nextInt(10000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
3.代理类(TankTimerProxy)
public class TankTimerProxy implements Moveable{ private Moveable t;//被代理对象 public TankTimerProxy(Moveable t){ this.t = t; } /** * 代理的move方法,前后加自定义逻辑 * @param o 代理对象 * @param method 方法处理逻辑 */ @Override public void move() { long start = System.currentTimeMillis(); t.move();//被代理对象的move方法。 long end = System.currentTimeMillis(); System.out.println("UsedTimes: " + (end - start)); } }
Java动态代理模拟:
如上:TankTimerProxy是我们手动编写的,也可称为静态代理,倘若能动态生成所需的代理类,如TankTimerProxy;则更加方便我们对被代理对象添加自定义处理逻辑。即,动态代理。
为模拟Java,声明Proxy类,使用Proxy类产生所需的各种具体代理对象,TankTimerProxy,TankLogProxy等。
import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * 代理的好处,可以对任意的对象,任意接口,实现任意的代理。 * @author dell * */ public class Proxy{ /** * 代理类名 */ private static final String PROXYNAME = "$proxy1"; /** * 模拟JDK代理 * @param infce 接口 * @param handler 代理处理逻辑 * @return * @throws Exception */ public static Object newProxyInstance(Class infce,InvocationHandler handler) throws Exception{ /** * Step1.1: * " @Override"+ rt + " public void move() {"+ rt + " long start = System.currentTimeMillis();"+ rt + " t.move();"+ rt + " long end = System.currentTimeMillis();"+ rt + " System.out.println(\"UsedTimes: \" + (end - start));"+ rt + " }"+ rt + */ String methodStr = ""; String rt = "\r\n"; Method[] methods = infce.getMethods(); /** * step:2 * 对方法的时间代理,且扩展麻烦。为增加不同的处理需重新创建newProxyInstance,为解决此问题, * 创建单独的处理逻辑InvocationHandler类 for (Method method : methods) { methodStr += " @Override"+ rt + " public void " + method.getName() + "() {"+ rt + " long start = System.currentTimeMillis();"+ rt + " t." + method.getName() + "();"+ rt + " long end = System.currentTimeMillis();"+ rt + " System.out.println(\"UsedTimes: \" + (end - start));"+ rt + " }"; } */ /** * Step3: * 为接口的所有方法,动态添加处理逻辑 */ for (Method method : methods) { methodStr += " @Override"+ rt + " public void " + method.getName() + "() {"+ rt + " try{" + rt + " Method md = " + infce.getName()+".class.getMethod(\""+method.getName()+"\");" +rt + " h.invoke(this,md);"+ rt + " }catch(Exception e){ e.printStackTrace();}"+ rt + " }"; } /** * Step:1 * 上面的TankTimerProxy为手动编写,现在我们用程序自动生成。 * 首先、生成TankTimerProxy类的字符串src,写入到磁盘文件 * 然后、编译生成的文件,生成class文件 * 最后、把生成的class文件load内存并创建TankTimerProxy类的对象 */ String src = "package com.lxf.proxy;" + rt + "import java.lang.reflect.Method;"+ rt + "public class " + PROXYNAME + " implements "+infce.getName()+"{"+ rt + " private InvocationHandler h;"+ rt + " public " + PROXYNAME + "(InvocationHandler h) {"+ rt + " this.h = h;"+ rt + " }" + rt + methodStr + rt + "}"; String fileName = "D:/src/com/lxf/proxy/" + PROXYNAME + ".java"; // String fileName = System.getProperties().getProperty("user.dir")+"/src/com/lxf/proxy/TankTimerProxy.java"; System.out.println(fileName); File file = new File(fileName); try { FileWriter fw = new FileWriter(file); fw.write(src); fw.flush(); fw.close(); } catch (IOException e) { e.printStackTrace(); } //compile JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null); Iterable units = fileMgr.getJavaFileObjects(fileName); CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units); t.call(); fileMgr.close(); //load into memory and create an instance URL[] urls = new URL[]{new URL("file:/"+"D:/src/")}; URLClassLoader ul = new URLClassLoader(urls);//使用classLoader时,必须保证.class文件在classpath路径中,所有为保证class可在任何位置, //使用URLClassLoader Class c = ul.loadClass("com.lxf.proxy." + PROXYNAME);//c:类的二进制码对象 System.out.println(c); Constructor cstr = c.getConstructor(InvocationHandler.class);//c.newInstance(),调用的是无参构造方法。 // Moveable m = (Moveable) cstr.newInstance(new Tank()); // m.move(); Object m = cstr.newInstance(handler); return m; } }自动生成的代理类存储在硬盘中,当然此类对用户是透明的,用户不用了解此列就可以实现对Tank的代理。
d:/src/com/lxf/proxy/$proxy1.java package com.lxf.proxy; import java.lang.reflect.Method; public class $proxy1 implements com.lxf.proxy.Moveable{ private InvocationHandler h; public $proxy1(InvocationHandler h) { this.h = h; } @Override public void move() { try{ Method md = com.lxf.proxy.Moveable.class.getMethod("move"); h.invoke(this,md); }catch(Exception e){ e.printStackTrace();} } }
为了方便的在方法前后添加处理逻辑(指定处理方式)如:添加日志,时间。即动态指定对方法的处理,所以定义InovactionHandler接口,定义代理对被代理对象的处理方式。
public interface InvocationHandler { /** * 代理处理方式 * @param o 代理对象 * @param method 被代理对象的方法 */ public void invoke(Object o, Method method); }
import java.lang.reflect.Method; /** * 自定义代理处理逻辑 * @author dell * */ public class TimerHandler implements InvocationHandler { /** * 被代理对象 */ private Object target; public TimerHandler(Moveable obj) { this.target = obj; } /** * 前后加自定义逻辑 * @param o 代理对象 * @param method 方法处理逻辑 */ @Override public void invoke(Object o, Method method) { long start = System.currentTimeMillis(); System.out.println("o Name: "+o.getClass().getName()); try { /** * 对带有指定参数的指定对象(target)调用由此 Method 对象表示的底层方法 * 然后,调用被代理对象的方法 */ method.invoke(target); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("UsedTimes: " + (end - start)); } }
客户端调用:
public class Client { public static void main(String[] args) throws Exception { Moveable tank = new Tank(); InvocationHandler ih = new TimerHandler(tank); Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class,ih); m.move(); } }
当然,上面使用了策略模式,方便对添加不同的处理逻辑。
至此,简单的Java 代理就模拟出来了。以上是自己听课(尚学堂:马士兵)《设计模式》所做记录,仅供学习。