[Java] 动态代理 04 - 想实现什么功能,就实现什么功能
6. 现在我们来解决下一个问题,我们每个接口都是来实现计时功能的?显然不是,那肯定还有其他功能三
那我们怎么样来做,才可以是我们想实现什么功能,就实现什么功能喃?动态代理?
我们按照一贯的做法,继续把功能也传经来。
Moveable.java
package com.bjsxt.proxy; public interface Moveable { void move(); }
被代理对象 Tank.java
package com.bjsxt.proxy; import java.util.Random; public class Tank implements Moveable { @Override public void move() { System.out.println("Tank Moving..."); try { Thread.sleep(new Random().nextInt(10000)); // 产生 100 毫秒 (10秒 ) 以内的随机数 } catch (InterruptedException e) { e.printStackTrace(); } } }InvocationHandler
package com.bjsxt.proxy; import java.lang.reflect.Method; public interface InvocationHandler { //指定方法(你需要时间,日志,还是其他) public void invoke(Object o,Method m) throws Exception; //告诉那个对象去执行这个方法 }TimeHandler
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) throws Exception { long start = System.currentTimeMillis(); System.out.println("Starttime : " + start); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("times : " + (end - start)); } }Proxy.java
package com.bjsxt.proxy; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; 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; import com.bjsxt.proxy.Moveable; import com.bjsxt.proxy.Tank; public class Proxy { // 这个类的作用就是用来产生新的代理类 public static Object newProxyInstance(Class infce, InvocationHandler h) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { // JDK6 Complier // API, CGLib, // ASM /* * 把这个类当成一个string的字符串(源码) 现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象 * * 动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法), * 会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的 */ String methodStr = ""; String rt = "\r\n"; Method ms[] = infce.getMethods(); /* * for (Method m : ms) { methodStr = "@Override" + rt + * " public void " + m.getName() + "() {" + rt + * " long start = System.currentTimeMillis();" + rt + * " t." + m.getName() + "();" + rt + * " long end = System.currentTimeMillis();" + rt + * " System.out.println((end - start));" + rt + " }"; } */ for (Method m : ms) { methodStr = 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) {" + rt + " 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 + rt + " com.bjsxt.proxy.InvocationHandler h;" + rt + methodStr + rt + "}"; // 获取当前系统目录(就是项目根目录) String fileName = "d:/src/com/bjsxt/proxy/$Proxy1.java"; System.out.println(fileName); // System.out.println(fileName); File f = new File(fileName); FileWriter writer = new FileWriter(f); writer.write(src); writer.flush(); writer.close(); // 看是否生成代码,右键项目,刷新就OK了 /* * 第三步 : 我们来生成一个类 */ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 拿到java的编译器 System.out.println(compiler.getClass().getName()); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);// 文件的 管理器 Iterable untis = fileMgr.getJavaFileObjects(fileName); // 找到文件,把文件放在 // Iterable(数组)中 CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, untis);// 定好编译文件任务 t.call(); // 编译文件 fileMgr.close();// 关闭文件管理器 /* * 运行 : * 编译之后,打开Navigator(这个可以看到类详细的变化,就是看得到class文件的产生),就会看到多了一个$Proxy1 * .class 文件,第三步成功 */ // Load into memory and create an instance /* * 第四步: 我们把文件加入内存(原本一般的做法是class.loader,就OK了,但是调用这个方法的前提就是, * 你的class文件目录必须在classpath的文件目录下),我们这里用一种通用的做法 */ // 这里使用url加载器 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); // 测试:输出c,OK,第四步完成 // 最后一步,生成对象 // 反射来创建对象 Constructor ctr = c.getConstructor(InvocationHandler.class); // 获取构造方法 Object m = (Object) ctr.newInstance(h); // m.move(); return m; } }生成的 TankTimeProxy
package com.bjsxt.proxy; import java.lang.reflect.Method; public class TankTimeProxy implements com.bjsxt.proxy.Moveable { public TankTimeProxy(InvocationHandler h) { this.h = h; } com.bjsxt.proxy.InvocationHandler h; @Override public void move() { try { Method md = com.bjsxt.proxy.Moveable.class.getMethod("move"); h.invoke(this, md); } catch (Exception e) { e.printStackTrace(); } } }生成的 $Proxy1
package com.bjsxt.proxy; import java.lang.reflect.Method; public class $Proxy1 implements com.bjsxt.proxy.Moveable { public $Proxy1(InvocationHandler h) { this.h = h; } com.bjsxt.proxy.InvocationHandler h; @Override public void move() { try { Method md = com.bjsxt.proxy.Moveable.class.getMethod("move"); h.invoke(this, md) ; } catch(Exception e) { e.printStackTrace(); } } }Client
package com.bjsxt.proxy; import java.io.Serializable; 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(); } } // 可以对任意的对象、任意的接口方法,实现任意的代理运行输出 :
d:/src/com/bjsxt/proxy/$Proxy1.java com.sun.tools.javac.api.JavacTool class com.bjsxt.proxy.$Proxy1 Starttime : 1390448630091 com.bjsxt.proxy.$Proxy1 Tank Moving... times : 7619