我们来自己写一个动态代理类,名字叫Proxy
源码:
package com.bjsxt.proxy; public class Proxy { //这个类的作用就是用来产生新的代理类 public static Object newProxyInstance() { // JDK6 Complier API, CGLib, ASM /* 把这个类当成一个string的字符串(源码) 现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象 动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法), 会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的 */ String rt = " "; String src = "package com.bjsxt.proxy;" + rt + rt + "public class TankTimeProxy implements Moveable {" + rt + " public TankTimeProxy(Moveable t) {" + rt + " this.t = t;" + rt + " }" + rt + rt + " Moveable t;" + rt + rt + " @Override" + rt + " public void move() {" + rt + " long start=System.currentTimeMillis();" + rt + " t.move();" + rt + " long end=System.currentTimeMillis();" + rt + " System.out.println((end - start));" + rt + " }" + rt + "}"; return null ; } }
上面的注释解释的很清楚了。
现在我们就来动态的编译这段代码
一般动态编译文件有这些方法(用JDK6的complier API(大于1.6都行,只是这个是1.6的新特性),CGlib,ASM(直接生成二进制的class文件))
我们直接用 JDK 的 complier
我们要做的步骤:
(1), 把字符串进行编译
(2), 生成一个类
(3), 写入内存
(4), 生成对象
下面我们就来一一实现, 我们先写一个测试类,叫 Test1.java
源码:
第一步 : 准备好字符串代码 String src
第二步 : 利用文件IO, 生成 TankTimeProxy
Test1.java
package com.bjsxt.compiler.test; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class Test1 { // 这个类的作用就是用来产生新的代理类 public static void main(String[] args) throws IOException { // JDK6 Complier API, CGLib, ASM /* * 把这个类当成一个string的字符串(源码) 现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象 * * 动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法), * 会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的 */ String rt = " "; String src = "package com.bjsxt.proxy;" + rt + rt + "public class TankTimeProxy implements Moveable {" + rt + " public TankTimeProxy(Moveable t) {" + rt + " this.t = t;" + rt + " }" + rt + rt + " Moveable t;" + rt + rt + " @Override" + rt + " public void move() {" + rt + " long start=System.currentTimeMillis();" + rt + " t.move();" + rt + " long end=System.currentTimeMillis();" + rt + " System.out.println((end - start));" + rt + " }" + rt + "}"; // 获取当前系统目录(就是项目根目录) String fileName = System.getProperty("user.dir") + "/src/com/bjsxt/proxy/TankTimeProxy.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了 } }
在做这一步之前,如果你的文件里有 TankTimeProxy.java 文件,你把它删除了,不需要了,因为我可以动态的来生成了。
运行代码,完成之后,右键项目,刷新,你会看到出现了一个 TankTimeProxy.java 文件 .OK ,第二步完成。
第三步 :我们来生成一个类
// 这句话的作用就是获取系统当前默认的编译器(其实就 javac)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 文件的产生),就会看到多了一个 TankTimeProxy.class 文件,第三步成功
第四步:
我们把文件加入内存(原本一般的做法是class.loader,就OK了,但是调用这个方法的前提就是,你的 class 文件目录必须在 classpath 的文件目录下),我们这里用一种通用的做法// 这里使用url加载器 URL[] urls = new URL[] { new URL("file:/" + System.getProperty("user.dir") + "/src") }; URLClassLoader ul = new URLClassLoader(urls); // 这里需要一个数组地址 Class c = ul.loadClass("com.bjsxt.proxy.TankTimeProxy"); // 把类加到内存 System.out.println(c);测试:输出c,OK,第四步完成
最后一步,生成对象
// 反射来创建对象
Constructor ctr = c.getConstructor(Moveable.class); // 获取构造方法 Moveable m = (Moveable) ctr.newInstance(new Tank()); // m 是用反射来创建的对象 m.move();
运行结果:
5.如果现在我们实现不是一个特定的接口(意思就是不是实现Moveable接口,而是实现的其他接口),那我们怎么办喃?
那我们把接口也当参数传进来
D:JavaMyEclipseWorkspacesProxy/src/com/bjsxt/proxy/TankTimeProxy.java com.sun.tools.javac.api.JavacTool class com.bjsxt.proxy.TankTimeProxy starttime : 1390141936679 Tank Moving... 6156Ok,我们要求的功能全部实现了。
5.如果现在我们实现不是一个特定的接口(意思就是不是实现Moveable接口,而是实现的其他接口),那我们怎么办喃?
那我们把接口也当参数传进来