1.什么是代理模式:
代理模式:为其他对象提供一种代理以便控制对这个对象的访问。
2.抛砖
之前做过一个坦克大战的例子(好像地球人都做过哈),假定有一个接口叫Moveable,坦克类Tank实现Moveable接口,必须实现move方法,如何计算这个move方法的执行时间呢?当然坦克类的代码是不能修改的。有两种办法:1.使用继承,让子类Tank2继承Tank,然后重写move方法,在重写的方法中计算父类move方法执行时间(super.move())。2.使用组合方式,新建一个类叫Tank3也实现Moveable接口,把Tank对象t作为Tank3的一个属性。然后重写move()方法,在move方法里面调用t的方法move方法,测出执行时间。
3.思考:以上两种方法哪个更好?显然组合更好。因为当有新需求时,使用继承方法时,需要重新定义一个类,比较麻烦,尤其是需求多变的时候。如果用组合呢?此时当有新需求时,将属性t声明为moveable,此时,多态的威力又来了。我们把tank3重新命名为TankTimeProxy类。新需求可以添加新的类TankLogProxy。代码如下:
Tank t =new Tank();
TankTimeProxyttp = new TankTimeProxy(t);
TankLogProxytlp = new TankLogProxy(ttp);
Moveablem = ttp;
m.move();
感觉有点装饰模式的意思哈。
4.至此就结束了吗?不是的。如果我再需要增加新的功能呢?如果每次都增加新的类,虽然满足设计基本原则,但会不会太麻烦,
因为每个类都很相似,只不过是增加了新的功能。能否用一种动态生成类的办法呢?也就是说类文件也自动生成。需要做如下步骤:
4.1 首先将代码拼装成字符串,参数是接口类型,使用了反射;
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
String methodStr = "";
String rt = "\r\n";
for(Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try {" + rt +
" Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String src =
"package com.bjsxt.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
" public $Proxy1(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" com.bjsxt.proxy.InvocationHandler h;" + rt +
methodStr +
"}";
4.2 将字符串写入指定文件;
String fileName =
"d:/src/com/bjsxt/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
4.3 获取系统的编译器,动态编译指定文件;
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();
4.4 获取系统的内存加载器,将指定目录中的文件加载到内存。
URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
System.out.println(c);
4.5 获取构造函数
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
通过以上的步骤,就动态的获取对象了。
5.注意参数问题,InvocationHandler 里面用代理处理的。
public class Client {
public static void main(String[] args) throws Exception {
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);
Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);
m.move();
}
}
package com.bjsxt.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.总结
6.1 代理模式本质:在代理类中关联另一个类,当调用代理类的方法时,本质上是执行被代理类的方法,实际上是被代理类对象在干活。
6.2 动态生成对象的本质:根据参数,自动生成代码,调用编译器动态编译,使用加载器自动将生成的对象加载到内存,返回给调用者。
//主测试文件
public class Client {
public static void main(String[] args) throws Exception {
Tank t = new Tank();
InvocationHandler h = new TimeHandler(t);
Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class, h);
m.move();
}
}
//Proxy.java文件/
package com.bjsxt.proxy;
import java.io.File;
import java.io.FileWriter;
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.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception { //JDK6 Complier API, CGLib, ASM
String methodStr = "";
String rt = "\r\n";
Method[] methods = infce.getMethods();
/*
for(Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" long start = System.currentTimeMillis();" + rt +
" System.out.println(\"starttime:\" + start);" + rt +
" t." + m.getName() + "();" + rt +
" long end = System.currentTimeMillis();" + rt +
" System.out.println(\"time:\" + (end-start));" + rt +
"}";
}
*/
for(Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try {" + rt +
" Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String src =
"package com.bjsxt.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
" public $Proxy1(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" com.bjsxt.proxy.InvocationHandler h;" + rt +
methodStr +
"}";
String fileName =
"d:/src/com/bjsxt/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//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);
Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");
System.out.println(c);
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
//m.move();
return m;
}
}
/InvocationHandler.java///
package com.bjsxt.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
//TimeHandler.java///
package com.bjsxt.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TimeHandler implements InvocationHandler{
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
System.out.println("starttime:" + start);
System.out.println(o.getClass().getName());
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}
///Moveable.java/
package com.bjsxt.proxy;
public interface Moveable {
void move();
}