设计模式--代理模式Proxy(学习笔记)

来源:互联网 发布:qq炫舞头像源码 编辑:程序博客网 时间:2024/05/22 01:50

代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

类图:

模拟情景:

1.有一个Tank类,实现Moveable接口中的move(),方法,现在想要对Tank的move()方法前后做些日志记录的操作,这时就可以使用代理。

public interface Moveable {void move();}

2.Tank类

import java.util.Random;/** * 被代理对象 * @author dell * */public class Tank implements Moveable{@Overridepublic 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 方法处理逻辑 */@Overridepublic void move() {long start = System.currentTimeMillis();                t.move();//被代理对象的move方法。long end = System.currentTimeMillis();System.out.println("UsedTimes: " + (end - start));}}

从上面的示例中可以看到,我们本来想使用Tank的move方法,现在通过TankTimerProxy的move()方法,间接的使用了Tank的方法,此时TankTimerProxy就是Tank的一个代理。


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();}//compileJavaCompiler 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可在任何位置,                                                              //使用URLClassLoaderClass 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.javapackage 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 方法处理逻辑 */@Overridepublic 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 代理就模拟出来了。以上是自己听课(尚学堂:马士兵)《设计模式》所做记录,仅供学习。


0 0
原创粉丝点击