我们来自己写一个动态代理类,名字叫Proxy
源码:
package com.bjsxt.proxy;
public class Proxy {
//这个类的作用就是用来产生新的代理类
public static Object newProxyInstance() { // JDK6 Complier API, CGLib, ASM
/* 把这个类当成一个string的字符串(源码)
现在我们假设,我们能把这字符串编译,生成类,放在内存,来产生对象
动态代理就是你看不到代理类,你只需要调用一个方法( Proxy的newProxyInstance()方法),
会自动给你返回一个代理类对象,这个对象的产生是由内部动态的生成一段代码,编译完成的
*/
String rt = "\r\n";
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 = "\r\n";
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:\Java\MyEclipse\Workspaces\Proxy/src/com/bjsxt/proxy/TankTimeProxy.java
com.sun.tools.javac.api.JavacTool
class com.bjsxt.proxy.TankTimeProxy
starttime : 1390141936679
Tank Moving...
6156
Ok,我们要求的功能全部实现了。
5.如果现在我们实现不是一个特定的接口(意思就是不是实现Moveable接口,而是实现的其他接口),那我们怎么办喃?
那我们把接口也当参数传进来