学习动态代理stepbystep(2)

学完了静态的,进行一个小小的进阶了。。。

这篇文章,是对java动态代理的一个小小的模拟。

有一个假设:被代理的类已经实现了一个接口[我们采用聚合方式]。(用继承也可以,但是不推荐)

模拟jdk的实现:

新建一个Proxy类,然后把TankTimeProxy.java文件中的内容,当作字符串复制到Proxy.java的一个方法中。

public class Proxy {
public static Object newProxyInstance(){

/*

* 动态代理,是解决类太多的问题。
* Q:如果下面这个可以编译,则就可以不用TankTimeProxy类了
*
* 这样产生代理的总代理
*/
String str =
"public class TankTimeProxy implements Moveable {" +
"public TankTimeProxy(Moveablet){" +
"super();" +
"this.t = t;" +
"}" +
"Moveablet;" +

"@Override" +
"public void move(){" +
"long start = System.currentTimeMillis();" +
"t.move();" + //在生成的文件中move方法会调用tank的,因为在Proxy.java中存在着Object o = (Object)ctr.newInstance(new Tank());
"long end = System.currentTimeMillis();" +
"System.out.println(\"time: \" + (end - start));" +
"}" +
"}";
return str;
}
}


/* 实现动态编译,有许多种方法
* 1.jdk6 的compilier
* 2.cglib
* 3.asm
* 后两者不需要编译了,直接生成2进制文件。
* 如果是接口,采用第一种机制
* */

现在,已经产生了一个X.java文件所需要的代码(字符串),我们需要对它进行的处理是: 1.生成.java文件 2.编译成.class 3.加载到内存中.4.生成真正的对象,这样,就可以供我们使用了。

一步一步来吧:

先放在main里面,这样方便对每一步即时测试嘛:

public class Proxy {
public static void main(String[] args) throws Exception{
String rt = "\r\n";
String str =
"public class TankTimeProxy implements Moveable {" + rt +
"\tpublic TankTimeProxy(Moveable t){" + rt +
"\tsuper();" + rt +
"\tthis.t = t;" + rt +
"\t}" + rt +
"\tMoveable t;" + rt +

"\t@Override" + rt +
"\tpublic void move(){" + rt +
"\t\tlong start = System.currentTimeMillis();" + rt +
"\t\tt.move();" + rt +
"\t\tlong end = System.currentTimeMillis();" + rt +
"\t\tSystem.out.println(\"time: \" + (end - start));" + rt +
"\t}" + rt +
"}";


1.先生成.java文件:

// user.dir:当前路径。如我的是:C:\Users\Seven\Workspaces\MyEclipse 8.6\Proxy

String fileName = System.getProperty("user.dir") + "/src/TankTimeProxy.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(str);
fw.flush();
fw.close();

(plus:后来的实践发现,当执行完这个程序的时候 ,MyEclipse已经在它自动生成class文件夹的目录下,生成了这个.class文件。)

2.对.java文件进行编译,生成.class文件:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

/*注意:让上面这句话编译通过可能需要调IDE的JRE,我用的M8.6,可以不调
System.out.println(compiler.getClass().getName());
输出为:com.sun.tools.javac.api.JavacTool
*/
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileManager, null, null, null, units);
t.call();
fileManager.close();


/*
* 此时,就生成.class文件了
* 在Navigator视图下,可以看到已经编译完成的.class文件,这个视图真实地反应了硬盘上的存储情况。
*但这样还不够,还要把它load到内存,再生成对象:用到了反射。
*load into memory and create instance
*classloader:必须保证class在classpath(bin)下面,但现在是在源码文件夹下。
*/

3.加载至内存

///哎,这里有个陷阱,"/src"的写法是不能变为它的目录的(原来找class文件与找java文件是不一样滴)

///只能在下面改:Class c = ul.loadClass("com.compiler.test.TankTimeProxy");

URL[] uris = new URL[] {new URL("file:/" + System.getProperty("user.dir") + "/src")};
URLClassLoader ul = new URLClassLoader(uris);
Class c = ul.loadClass("TankTimeProxy"); //此处的名字应该与上面遥相呼应(这样就是放在了根目录下的情况)
System.out.println(c);


4.生成真正的对象

/*通过上面的操作,已经把我们需要的类load到内存了,但是还要生成对象
其实也可以直接生成到bin文件夹下,只是学会一种随意load的方法
在实际中,可以动态生成str,即java代码

下一步:生成对象。
通常:c.newInstance();生成一个对象,但是,这是生成构造函数为空的对象,而我们都是有带参数的。
所以得换其他的方法。
*/

//这句话是说,我要建立一个新的构造方法,而这个构造方法的参数,要是Movealbe类型的
Constructor ctr = c.getConstructor(Moveable.class); //拿到构造方法
Moveable m = (Moveable)ctr.newInstance(new Tank()); //这样就会调用含参的构造方法,传入参数,正式生成对象
m.move();

///可以随意改变类名:TankTimeProxy.java
/*然后,删除掉.java文件,这样,就可以了。*/





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值