好的,接下来我们接着上一篇博客接着继续给大家介绍java动态代理技术,上一篇博客的结尾我们讲到我们要用自己的模拟的Proxy类来实现跟JDK的Proxy类差不多的功能,即返回一个代理类。以下内容需要读者有一定java反射基础,如不了解,可先去看下我的另外一篇博文:java反射技术
我们首先分析下,JDK的Proxy类的newProxyInstance接收三个参数,第一个是ClassLoader,我们为了简单起见,省去这个参数,ClassLoader的具体作用读者感兴趣可上网了解,第二个参数是被代理类实现的接口列表,我们为简单起见,默认被代理类只实现一个接口。
先简单说下JDK的Proxy类干了什么,然后再模拟它,它接收了被代理类实现的接口列表目的是创造一个同样实现这些接口的代理类,这样他就能反射出这些接口中的方法,也就是他可以得到被代理类中的方法列表,然后它还接收一个InvocationHandler的实现类h,在反射出的每一个方法中,会调用h的invoke方法,在h的invoke方法中,先加入自己的逻辑,然后再调用被代理类中的同样方法(注意,h中持有一个被代理对象),将所有的方法反射完成之后,动态编译这个代理类,返回一个代理类的对象。然后我们在外面就可以直接拿接口类型的引用接收这个对象,当调用这个代理类的方法的时候,我们自己加的逻辑自然也就加进去了。
上面只是说的是基本原理,当然JDK的Proxy类在实际执行过程中远比以上描述要复杂,不知各位读者,你们明白了么?如果还不明白的话,我们直接上代码把。
package com.proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import com.proxy.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class Proxy {
//接收一个接口的Class对象,然后接收一个InvocationHandler的对象 h
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
String methodStr = "";
String rt = "\r\n";
//反射出接口中的所有方法
Method[] methods = infce.getMethods();
for(Method m : methods) {
//对每个方法,我们构造他们的方法字符串,为简单起见,我们默认每个方法都没有返回值
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try {" + rt +
//得到该方法的名字
" Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
//调用h的invoke方法,至于invoke方法内部执行了什么,可去h中看下
" h.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String src =
"package com.proxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"import com.proxy.InvocationHandler;" + rt +
//代理类和被代理类实现同样的接口
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
//代理类持有一个InvocationHandler对象,为简单起见,我们自己重新定义一个InvocationHandler接口和实现类
" public $Proxy1(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" InvocationHandler h;" + rt +
methodStr +
"}";
//以下代码为动态编译过程,将以上代码写到一个java文件中,然后编译此文件,在根据编译生成的class文件反射出对象
String fileName =
"d:/src/com/proxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
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();
URL[] urls = new URL[] {new URL("file:/" + "d:/src/")};
URLClassLoader ul = new URLClassLoader(urls);
Class c = ul.loadClass("com.proxy.$Proxy1");
//反射出那个接受一个InvocationHandler对象为参数的构造方法
Constructor ctr = c.getConstructor(InvocationHandler.class);
//得到一个代理对象,然后返回,至此,大功告成
Object m = ctr.newInstance(h);
//m.move();
return m;
}
}
为简单起见,我们重写了InvocationHandler接口(上面代码中用到的),如下所示:
package com.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
我们重新定义了InvocationHandler的实现类,如下所示:
package com.proxy;
import com.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class HelloWorldHandler1 implements InvocationHandler {
// 持有一个Object对象,用来保存被代理对象
Object obj = null;
public HelloWorldHandler1(Object obj) {
this.obj = obj;
}
@Override
public void invoke(Object proxy, Method method) {
// 在方法执行之前加上自己的逻辑
this.before();
// 执行被代理对象的方法
try {
Object resultObject = method.invoke(obj, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 在方法执行之后加上自己的逻辑
this.after();
// 返回方法执行的结果,当然可能为null
}
public void before() {
System.out.println("Method sayHello start");
}
public void after() {
System.out.println("Method sayHello end");
}
}
测试类如下:
package com.proxy;
import com.proxy.Proxy;
public class HelloWorldTest3 {
public static void main(String[] args) throws Exception {
HelloWorld helloWorld = new HelloWorldImpl();
HelloWorldHandler1 helloWorldHandler = new HelloWorldHandler1(helloWorld);
// 调用Proxy的静态方法newProxyInstance创建一个代理类
HelloWorld helloWorld2 = (HelloWorld) Proxy.newProxyInstance(HelloWorld.class,
helloWorldHandler);
// 调用代理类的同名方法sayHello
helloWorld2.sayHello();
}
}
以上代码也实现了对HelloWorldImpl的动态代理,但是由于简化了结构,所以功能还是很有限,但是基本的原理就是这个样子滴,大家可以自己研究下我们自己模拟的Proxy类,有什么疑问或者问题,欢迎在评论中提出,我将尽可能回复大家。
我上面讲到了,阅读上面那个Proxy类需要一定的java反射基础,理解了上面那个类的原理,也就基本上理解JDK的Proxy的基本原理了。
希望以上内容对大家学习java动态代理技术有一定的帮助,谢谢。