代理模式(三)之模仿jdk底层动态代理实现

前言

在前面的基础上在做进一步的改进,前面我们是动态写死了方法,即自己指定了方法名,当然我们可以利用反射帮我们完成函数名的动态指定

首先是一个反射例子

1
2
3
4
5
6
//拿到相应的类
Method[] methods=com.zwl.pr.Jungle.class.getMethods();
for(Method method: methods){
//循环输出该类的函数名,如public void ss();最后只输出ss
System.out.println(method.getName());
}

有了这个原理就可以对之前的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
63
64
65
66
67
68
69
70
71
72
73
public static Object newProxyInstance(Class interfac) throws Exception{
//动态拿到方法的代码
String methodStr = "";
String rt = "\r\n";
//将指定的动态接口类名拿到
Method[] methods = interfac.getMethods();
//循环添加方法
for(Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" long start = System.currentTimeMillis();" + rt +
" leesin." + m.getName() + "();" + rt +
" long end = System.currentTimeMillis();" + rt +
" System.out.println(\"time:\" + (end-start));" + rt +
"}";
}
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 +
//这里循环动态输出方法
methodStr +
"}" ;
//System.out.println(System.getProperty("user.dir"));
//这里将动态生成的代理文件放到指定文件夹中,避免与bin里的.class文件冲突
String filename=
"E:/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:/" + "E:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.zwl.pr.LeeSinTimeProxy");
System.out.println(c);
Constructor ctr = c.getConstructor(Jungle.class);
//返回任意一个类
Object jungle=ctr.newInstance(new LeeSin());
return jungle;
}
注意这里和之前做了几处修改
1.利用反射动态生成方法
2.更改编译后生成文件的目录
3.返回类型为object
4具体在代码里有注释

测试类:
1
2
Jungle jungle=(Jungle) Proxy.newProxyInstance(Jungle.class);
jungle.gank();

输出结果

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

这里很明显发现方法都是固定的,如果想要方法都是动态生成的,则要进行进一步修改
首先增加一个
InvocationHandler动态生成类

1
2
3
public interface InvocationHandler {
public void invoke(Object o, Method m);
}

然后是具体的针对所有接口的时间处理类

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
public class TimeHandler implements InvocationHandler {
//代理类
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
long start = System.currentTimeMillis();
try {
//指定代理类的动态方法
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time:" + (end-start));
}
}

然后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
63
64
65
66
67
68
69
70
71
//参数分别为被代理对象与代理对象
public static Object newProxyInstance(Class interfac,InvocationHandler h) throws Exception{
String methodStr = "";
String rt = "\r\n";
Method[] methods = interfac.getMethods();
//动态生成方法,其中this是指代理对象本身,而md是指方法名,这里的h是构造函数传的代理对象
for(Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try {" + rt +
" Method md = " + interfac.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String str=
"package com.zwl.pr;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class LeeSinTimeProxy implements "+interfac.getName()+ "{" + rt +
"com.zwl.pr.InvocationHandler h;" + rt +
"public LeeSinTimeProxy(InvocationHandler h) {" + rt +
"super();" + rt +
"this.h = h;" + rt +
"}" + rt +
methodStr +
"}" ;
//System.out.println(System.getProperty("user.dir"));
String filename=
"E:/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:/" + "E:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.zwl.pr.LeeSinTimeProxy");
System.out.println(c);
//返回代理类
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object jungle=ctr.newInstance(h);
return jungle;
}

测试类:

1
2
3
4
5
6
//被代理对象
LeeSin leesin=new LeeSin();
//代理对象
InvocationHandler ivo=new TimeHandler(leesin);
Jungle jungle=(Jungle) Proxy.newProxyInstance(Jungle.class,ivo);
jungle.gank();

对应生成的java文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.zwl.pr;
import java.lang.reflect.Method;
public class LeeSinTimeProxy implements com.zwl.pr.Jungle{
com.zwl.pr.InvocationHandler h;
public LeeSinTimeProxy(InvocationHandler h) {
super();
this.h = h;
}
@Override
public void gank() {
try {
Method md = com.zwl.pr.Jungle.class.getMethod("gank");
h.invoke(this, md);
}catch(Exception e) {e.printStackTrace();}
}}

对应的测试结果

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

热评文章