我们继续上一篇总结。
我们把TankTimeProxy的类Load进内存之后我们要生成它的一个对象。我们先来回顾一下我们之前写好的Proxy类:
package cn.edu.hpu.ProxyTest; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; 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; import cn.edu.hpu.proxy.Moveable; public class Test1 { public static void main(String[] args) throws Exception{ String rt="\r\n"; String src= "package cn.edu.hpu.proxy;"+ rt + "public class TankTimeProxy implements Moveable{"+ rt + " Moveable t;"+ rt + " long start;"+ rt + " long end;"+ rt + " public TankTimeProxy(Moveable t) {"+ rt + " super();"+ rt + " this.t = t;"+ rt + " }"+ rt + " public void before(){"+ rt + " start=System.currentTimeMillis();"+ rt + " System.out.println(\"开始时间:\"+start+\"ms\");"+ rt + " }"+ " public void after(){"+ rt + " end=System.currentTimeMillis();"+ rt + " System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt + " }"+ rt + " @Override"+ rt + " public void move() {"+ rt + " this.before();"+ rt + " t.move();"+ rt + " this.after();"+ rt + " }"+ rt + "}"; //拿到当前项目的根目录:System.getProperty("user.dir")); String fileName=System.getProperty("user.dir") +"/src/cn/edu/hpu/proxy/TankTimeProxy.java"; //我们把src的源码写入自己创建的File文件中去 File f=new File(fileName); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac) JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); //需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化) StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null); //通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中 //Iterable是一个数组,用它可以进行迭代 Iterable units=fileMgr.getJavaFileObjects(fileName); //参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件) CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units); //进行编译 t.call(); fileMgr.close(); //把编译好的.class文件加载到内存中 //urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类) URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")}; //ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类 URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy"); System.out.println(c); } }
我们已经拿到TankTimeProxy类的class类,下面我们来得到它的对象:
//得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型 Constructor ctr=c.getConstructor(Moveable.class); Moveable m=(Moveable)ctr.newInstance(new Tank()); m.move();
运行结果:
class cn.edu.hpu.proxy.TankTimeProxy
开始时间:1436429931050ms
坦克正在移动中...
运行时间:6313ms
我们把Test1的所有代码转移到Proxy类中。我们的整个的Proxy类的结构如下:
package cn.edu.hpu.proxy; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; 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() throws Exception{ String rt="\r\n"; String src= "package cn.edu.hpu.proxy;"+ rt + "public class TankTimeProxy implements Moveable{"+ rt + " Moveable t;"+ rt + " long start;"+ rt + " long end;"+ rt + " public TankTimeProxy(Moveable t) {"+ rt + " super();"+ rt + " this.t = t;"+ rt + " }"+ rt + " public void before(){"+ rt + " start=System.currentTimeMillis();"+ rt + " System.out.println(\"开始时间:\"+start+\"ms\");"+ rt + " }"+ " public void after(){"+ rt + " end=System.currentTimeMillis();"+ rt + " System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt + " }"+ rt + " @Override"+ rt + " public void move() {"+ rt + " this.before();"+ rt + " t.move();"+ rt + " this.after();"+ rt + " }"+ rt + "}"; //拿到当前项目的根目录:System.getProperty("user.dir")); String fileName=System.getProperty("user.dir") +"/src/cn/edu/hpu/proxy/TankTimeProxy.java"; //我们把src的源码写入自己创建的File文件中去 File f=new File(fileName); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac) JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); //需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化) StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null); //通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中 //Iterable是一个数组,用它可以进行迭代 Iterable units=fileMgr.getJavaFileObjects(fileName); //参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件) CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units); //进行编译 t.call(); fileMgr.close(); //把编译好的.class文件加载到内存中 //urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类) URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")}; //ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类 URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy"); System.out.println(c); //得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型 Constructor ctr=c.getConstructor(Moveable.class); Moveable m=(Moveable)ctr.newInstance(new Tank()); return m; } }
到此为止,我们就自己写了一个生成动态代理的类。自己想往这个动态代理中写任何东西都可以。
我们虽然实现了一个动态代理,但是貌似我们只实现了Moveable接口的类的动态代理。如果换成实现了其它接口的类还是不行。那是不是可以产生一个对实现任意接口的类的代理?
思路:在Proxy类中,我们产生newProxyInstance()的时候,告诉程序你要实现哪个接口就可以了。
//这个方法用来产生新的代理类 //参数:告诉程序使用实现哪个接口的动态代理 public static Object newProxyInstance(Class infce) throws Exception{ String rt="\r\n"; String src= "package cn.edu.hpu.proxy;"+ rt + "public class TankTimeProxy implements "+infce.getName()+"{"+ rt + ...... }
测试:
package cn.edu.hpu.proxy; public class Client { public static void main(String[] args) throws Exception { Moveable m=(Moveable)Proxy.newProxyInstance(Moveable.class); m.move(); } }
测试结果:
class cn.edu.hpu.proxy.TankTimeProxy
开始时间:1436430626384ms
坦克正在移动中...
运行时间:9612ms
我现在想让它去实现其它接口的代理,也完全可以。
下面我们进行下一步的模拟。我们虽然可以换接口,但是我们下面的代码还是只是实现了一个move()方法,而不是我们实现的接口的方法,严格来说,实现的接口有多少方法,我们就应该给多少个方法生成代码。那么怎么知道这个Class中有多少个方法呢?
我们再写一个反射的测试:
我们想要知道一个类中有多少个方法,其实很简单:
package cn.edu.hpu.ProxyTest; import java.lang.reflect.Method; public class Test2 { public static void main(String[] args) { //使用了Java的反射机制中的getMethods();方法来得到一个类的所有方法 Method[] methods=cn.edu.hpu.proxy.Moveable.class.getMethods(); for (Method m:methods) { System.out.println(m.getName()); } } }
测试结果:
move
既然我们能拿到某各类所有方法,对于我们代理类的方法这么改写:
package cn.edu.hpu.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) throws Exception{ String methodStr=""; String rt="\r\n"; //使用了Java的反射机制中的getMethods();方法来得到一个类的所有方法 Method[] methods=infce.getMethods(); for (Method m:methods) { methodStr += rt+" @Override"+rt+ " public void "+m.getName()+"() {"+rt+ " this.before();"+ rt + " t."+m.getName()+"();"+ rt + " this.after();"+ rt + " }"; //返回值通过反射机制也可以拿到,但是比较麻烦,我们这里暂时用void代替 } String src= "package cn.edu.hpu.proxy;"+ rt + "public class TankTimeProxy implements "+infce.getName()+"{"+ rt + " Moveable t;"+ rt + " long start;"+ rt + " long end;"+ rt + " public TankTimeProxy(Moveable t) {"+ rt + " super();"+ rt + " this.t = t;"+ rt + " }"+ rt + " public void before(){"+ rt + " start=System.currentTimeMillis();"+ rt + " System.out.println(\"开始时间:\"+start+\"ms\");"+ rt + " }"+ " public void after(){"+ rt + " end=System.currentTimeMillis();"+ rt + " System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt + " }"+ rt + methodStr+ rt + "}"; //拿到当前项目的根目录:System.getProperty("user.dir")); String fileName=System.getProperty("user.dir") +"/src/cn/edu/hpu/proxy/TankTimeProxy.java"; //我们把src的源码写入自己创建的File文件中去 File f=new File(fileName); FileWriter fw=new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //编译(getSystemJavaCompiler()拿到系统默认的Java编译器,其实就是javac) JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); //需要一个FileManager,用它来管理文件(参数1:诊断的监听器,参数2、3国际化) StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null); //通过FileManager找到TankTimeProxy文件,然后放到一个Iterable中 //Iterable是一个数组,用它可以进行迭代 Iterable units=fileMgr.getJavaFileObjects(fileName); //参数:(输出位置,文件管理器对象,监听器,编译的时候指定的参数,用到那些class文件,需要编译哪些文件) CompilationTask t=compiler.getTask(null, fileMgr, null, null, null, units); //进行编译 t.call(); fileMgr.close(); //把编译好的.class文件加载到内存中 //urls指定.class文件所放的地方(使用URL还可以Load从网上传过来的类) URL[] urls=new URL[]{new URL("file:/"+System.getProperty("user.dir")+"/src")}; //ClassLoader指的是吧硬盘里的Java文件放到内存中的那些个类 URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("cn.edu.hpu.proxy.TankTimeProxy"); System.out.println(c); //得到TankTimeProxy类的构造方法,构造方法的参数为Moveable类型 Constructor ctr=c.getConstructor(Moveable.class); Object m=ctr.newInstance(new Tank()); return m; } }
这样我们就可以生成实现任意接口的对象的代理类了。
测试:
package cn.edu.hpu.proxy; public class Client { public static void main(String[] args) throws Exception { Moveable m=(Moveable)Proxy.newProxyInstance(Moveable.class); m.move(); } }
生成的TankTimeProxy
package cn.edu.hpu.proxy; public class TankTimeProxy implements cn.edu.hpu.proxy.Moveable{ Moveable t; long start; long end; public TankTimeProxy(Moveable t) { super(); this.t = t; } public void before(){ start=System.currentTimeMillis(); System.out.println("开始时间:"+start+"ms"); } public void after(){ end=System.currentTimeMillis(); System.out.println("运行时间:"+(end-start)+"ms"); } @Override public void move() { this.before(); t.move(); this.after(); } }
所以我们现在实现了这样一个功能:往newProxyInstance(Class infce)传任意接口,就能生成一个实现了某接口的类的对象。我们的“动态代理”又近了一步。
但是还有一个特别麻烦的事,我们现在在自己代理的内容是写死的。。
比如
" public void before(){"+ rt + " start=System.currentTimeMillis();"+ rt + " System.out.println(\"开始时间:\"+start+\"ms\");"+ rt + " }"+ " public void after(){"+ rt + " end=System.currentTimeMillis();"+ rt + " System.out.println(\"运行时间:\"+(end-start)+\"ms\");"+ rt
这段,里面的代码是写死的,我们只能实现时间上的代理,我们想实现其它类型代理实现不了...
怎么让before()和after()中的内容也让客户灵活指定?
不管怎么样,我们现在需要一个这样的东西:可以动态指定对方法进行处理的指令。
我们下篇总结继续
转载请注明出处:http://blog.csdn.net/acmman/article/details/46828187