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

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

类图:

模拟情景:

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));
	}

}

从上面的示例中可以看到,我们本来想使用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();
		}
		
		//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 代理就模拟出来了。以上是自己听课(尚学堂:马士兵)《设计模式》所做记录,仅供学习。


你可能感兴趣的:(java,设计模式)