代理模式(二)之初识动态代理

前言

之前我们通过静态代理可以在编译期创建一个代理对象,如果要求我们必须在运行时创建一个代理,如时间代理,如何做了?
也就是说,需要写一个代理(proxy),然后我们只需要传入相关接口,它就可以主动生成代理对象

再一次使用之前的情景,即有一个打野对象接口和具体打野盲僧,然后编写一个针对打野的动态代理(proxy),只要传入(打野接口对象),就可以动态自动生成该java文件,并且载入内存并不jvm编译

下面就是其代码实现

Jungle(打野接口)

1
2
3
4
public interface Jungle {
//打野gank方法
public void gank();
}

LeeSin(盲僧)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LeeSin implements Jungle {
@Override
public void gank() {
//gank开始时间
long starts=System.currentTimeMillis();
System.out.println("LeeSin is ganking...");
try {
//gank进行中
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//gank结束时间
long ends=System.currentTimeMillis();
System.out.println("gank time:"+(ends-starts));
}
}

如果想要动态生成一个事件代理(按常理应该新建一个代理类),按照动态思想,我们将该创建该类的所以涉及的代码赋给一个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String str=
"package com.zwl.pr;" +
"public class LeeSinTimeProxy implements Jungle {" +
"Jungle leesin;" +
"public LeeSinTimeProxy(Jungle leesin) {" +
"super();" +
"this.leesin = leesin;" +
"}" +
"@Override" +
"public void gank() {" +
"long starts=System.currentTimeMillis();" +
"leesin.gank();" +
"long ends=System.currentTimeMillis();" +
" System.out.println(\"gank time:\"+(ends-starts));" +
"}" +
"}" ;

上面字符串就是创建该代理的代码,然后如果jvm能自动将其编译成.class并且载入内存,就是一个真的类了,按照这种思想,我们可以通过jvm提供的方法完成
为了方便先把上述动态创建时间代理放在测试中(也就是main函数中)
1.首先将代码格式修整一下,即加个换行,同时将字符写入时间代理文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//换行,防止所有代码全部挤在一行
String rt = "\r\n";
//要动态生成的类的代码
String str=
"package com.zwl.pr;" + rt +
"public class LeeSinTimeProxy implements Jungle {" + rt +
"Jungle leesin;" + rt +
"public LeeSinTimeProxy(Jungle leesin) {" + rt +
"super();" + rt +
"this.leesin = leesin;" + rt +
"}" + rt +
"@Override" + rt +
"public void gank() {" + rt +
"long starts=System.currentTimeMillis();" + rt +
"leesin.gank();" + rt +
"long ends=System.currentTimeMillis();" + rt +
" System.out.println(\"gank time:\"+(ends-starts));" + rt +
"}" + rt +
"}" ;
//得到该项目的根路径
//System.out.println(System.getProperty("user.dir"));
//该时间代理文件的完整路径,不存在则创建(第一次自动动态创建)
String filename=System.getProperty("user.dir")+
"/src/com/zwl/pr/LeeSinTimeProxy.java";
//将字符写入该文件
File f=new File(filename);
FileWriter fw=new FileWriter(f);
fw.write(str);
fw.flush();
fw.close();

这里先一小段一小段的加,目前是已经在该包下生成一个LeeSinTimeProxy.java文件,并且里面有被写入的字符,但是纯粹只是一个文件,因为还没被编译和载入内存

2.进行编译

1
2
3
4
5
6
7
8
9
10
11
12
//compile
//得到java编译器,也就是javac了,至此我们可以最终将其编译,得到.class(二进制文件)
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//管理编译文件,默认为空就行
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
//所有编译的文件,目前就一个,当然可以传入多个
Iterable units = fileMgr.getJavaFileObjects(filename);
//编译任务管理,目前传入上面两个参数,其余默认为空就行
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
//执行任务编译
t.call();
fileMgr.close();

至此,在包下也动态生成了一个.class文件,而平常我们编译时都会在path路径下,jdk主动生成.class,而此时我们自己指定了位置

3.载入内存

1
2
3
4
5
6
//指定载入内存的url
URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+"/src")};
URLClassLoader ul = new URLClassLoader(urls);
//指定class文件载入
Class c = ul.loadClass("com.zwl.pr.LeeSinTimeProxy");
System.out.println(c);

4.利用反射取出实例

1
2
3
4
5
6
7
//反射取出类
Constructor ctr = c.getConstructor(Jungle.class);
//操作该类相应方法,此处是动态返回类
Jungle jubgle= (Jungle) ctr.newInstance(new LeeSin());
//调用gank方法
jubgle.gank();

以下是结果:

class com.zwl.pr.LeeSinTimeProxy
LeeSin is ganking…
gank time:7557
gank time:7563

至此算是动态生成了时间代理,但是目前是很有局限性的,因为我们指定了一个特定接口,而不是泛指,当然通过这个例子,也算是初识了一把动态模式的魅力,当然它的最大魅力,在于制定任何接口,返回相应的代理类

在这里我们先将测试的类中代码移至Proxy类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Proxy {
//这里的interfac就是动态指定了任何接口代理,而且必须取它的名字,否则是执行其tostring方法
public static Object newProxyInstance(Class interfac) throws Exception{
String rt = "\r\n";
String str=
"package com.zwl.pr;" + rt +
"public class LeeSinTimeProxy implements "+interfac.getName()+ "{" + rt +
"Jungle leesin;" + rt +
"public LeeSinTimeProxy(Jungle leesin) {" + rt +
"super();" + rt +
"this.leesin = leesin;" + rt +
"}" + rt +
"@Override" + rt +
"public void gank() {" + rt +
"long starts=System.currentTimeMillis();" + rt +
"leesin.gank();" + rt +
"long ends=System.currentTimeMillis();" + rt +
" System.out.println(\"gank time:\"+(ends-starts));" + rt +
"}" + rt +
"}" ;
//System.out.println(System.getProperty("user.dir"));
String filename=System.getProperty("user.dir")+
"/src/com/zwl/pr/LeeSinTimeProxy.java";
File f=new File(filename);
FileWriter fw=new FileWriter(f);
fw.write(str);
fw.flush();
fw.close();
//compile
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(filename);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//load into memory and create an instance
URL[] urls = new URL[] {new URL("file:/" + System.getProperty("user.dir")+"/src")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.zwl.pr.LeeSinTimeProxy");
System.out.println(c);
Constructor ctr = c.getConstructor(Jungle.class);
Jungle jubgle= (Jungle) ctr.newInstance(new LeeSin());
jubgle.gank();
return null;
}
}

目前先不管错误,在做测试类

1
2
3
LeeSin leesin=new LeeSin();
Jungle jungle=(Jungle) Proxy.newProxyInstance(Jungle.class);
jungle.gank();

这里参数我们可以设置任何class接口,当然先不管错误,至少动态代理算是成功了。

热评文章