深度模拟java动态代理实现机制系类之一
上一篇博客是最基本的动态代理原理的实现,因为其固定了接口,固定了代理方法,以及固定了代理的类型,
接下来的博客系类将一步步渐入深度介绍java的动态代理的实现原理
****************************************************************************
首先补充一下“java代理”的相关知识,静态代理的实现包括两种方式,一是聚合,另一种是继承。
聚合是指通过接口,调用其实现类的具体方法:比如接口i,含有方法run(); 类A 实现了接口i,当然
也实现了方法run(); 类B于是就可以通过new一个接口 i 的对象,调用A的run()方法,并在run()方法前后实现其他操作,
这样就实现了对A的run()方法的代理;
继承当然更好理解,就是把run()方法重写,从而实现代理
但是,一旦代理的操作很多,需要写的类都非常的繁杂,就需要不断的写代理类,不断的更新代理操作,于是这就有了动
态代理。
*****************************************************************************
这次要对实现任意接口的类进行代理。
1、接口
public interface Moveable { void move(); }
2、被代理的对象
1 public class Tank implements Moveable { 2 3 @Override 4 public void move() { 5 6 System.out.println("Tank Moving..."); 7 try { 8 Thread.sleep(new Random().nextInt(10000)); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 13 } 14 15 }
3、用于产生代理对象
1 public class Proxy { 2 //产生新的动态代理类 3 public static Object newProxyInstance(Class intf) throws Exception{ //将接口当成参数传入,这样就可以代理实现了任意接口的类,而不仅是实现了Moveable接口 4 //将一下字符串动态编译,生成代理类 5 //实现方式有以下击中:jdk6.0 complier API;CGLib; ASM 6 String rt = "\r\n"; 7 String src = 8 "public class TankTimeProxy implements " +intf.getName()+ "{"+rt+ //直接用intf,是调用toString方法,前会加入字符串 interface 9 intf.getName()+" t;"+rt+ 10 11 " public TankTimeProxy("+intf.getName()+" t) {"+rt+ 12 " this.t = t;"+rt+ 13 " }"+rt+ 14 15 " @Override"+rt+ 16 " public void move() {"+rt+ 17 " long start = System.currentTimeMillis();"+rt+ 18 " System.out.println(\"start time is \"+start);"+rt+ 19 " t.move();"+rt+ 20 " long end = System.currentTimeMillis();"+rt+ 21 " System.out.println(\"end time is \"+end);"+rt+ 22 " System.out.println(\"time is \"+(end - start));"+rt+ 23 " }"+rt+ 24 "}"; 25 26 //进行编译 27 String fileName = "g:/src/TankTimeProxy.java";//将文件另外存储,不放置在工程的默认路径,防止缓冲区相关类的冲突 28 File f = new File(fileName); 29 FileWriter fw = new FileWriter(f); 30 //System.out.println(fileName); 31 fw.write(src); //写入内容 32 33 fw.flush(); 34 fw.close(); 35 36 //进行编译 37 //首先获得编译器 38 //compiler 为java编译器 javac 39 //获得编译器对象 40 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 41 //System.out.println(compiler.getClass().getName());//取得类名 42 //参数含义 (编译诊断,locale,charset) 43 //管理动态生成的文件 44 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null,null);//默认值 45 //根据参数获取多个java文件 返回java文件对象 46 Iterable units = fileManager.getJavaFileObjects(fileName); 47 48 //“编译任务”对象 49 JavaCompiler.CompilationTask task = compiler.getTask(null,fileManager,null,null,null,units); 50 task.call();//调用 51 fileManager.close(); 52 53 //************以上过程获得了java文件源码,编译生成了java文件的class文件******* 54 //加载至内存,生成新对象 55 //Class.load(0 是加载path路径的class文件 56 //URLClassLoader是将硬盘中的class文件加载进入 57 58 //通过Url引入本地文件 59 URL[] urls = new URL[]{new URL("file:/"+"g:/src/")}; //指定生成class文件的位置,与java文件放置在同一目录 60 //去指定路径寻找class文件 61 URLClassLoader urlClassLoader = new URLClassLoader(urls); 62 63 Class c = urlClassLoader.loadClass("TankTimeProxy"); 64 65 System.out.println(c); 66 67 //执行 68 //c.newInstance(); 是调用空的构造方法 69 70 //获得构造方法 71 //根据java虚拟机,每一个构造方法也相当于一个对象 72 Constructor constructor = c.getConstructor(intf); 73 74 //产生新对象 75 Moveable m = (Moveable) constructor.newInstance(new Tank()); //new Tank()为构造方法的参数 即被代理对象 76 77 m.move(); 78 79 return m; 80 } 81 }
4、测试端
1 public class Client { 2 public static void main(String[] args) throws Exception { 3 4 //这里任然以Moveable接口为例传入,因为在Proxy中为节省麻烦,还是固定生成了Moveable,后来会慢慢简化的,其实是可以传递任意的的接口的 5 Moveable m =(Moveable) Proxy.newProxyInstance(Moveable.class); 6 m.move(); 7 8 } 9 }
5、结果
(1)生成的java与class文件
(2)java文件的代码
(3)运行结果