1. 先看JDK提供代理类Proxy
根据JDK描述,动态代理类Proxy主要用来创建动态代理类,是一个在创建类时实现指定的接口列表的类,这个指定的接口叫做代理接口,即是创建代理类实现的接口,代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,这个调用处理程序对象实现接口 InvocationHandler;代理类具有以下属性:
根据JDK的约定,调用处理对象必须包含一个代理实例,如上private Object target表示代理对象。
"$Proxy"
开头的类名空间应该为代理类保留。
代理类会按同一顺序准确地实现其创建时指定的接口。
在看JDK中Proxy类产生代理类的一个静态方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
该方法返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,其中interfaces表示代理类要实现的接口列表;h为指派方法调用的调用处理程序;
java.lang.reflect.InvocationHandler接口定义一个方法如下;
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable
proxy为代理实例,Method为代理实例和代理实现的接口中定义的方法;
2.显然上面的描述是比较抽象,下面我写一个自己的Proxy类和InvocationHanlder接口:
如下为为我定义的InvocationHanlder:
package com.learn.proxy;
import java.lang.reflect.Method;
public interface InvocationHanlder {
public void invoke(Object proxy, Method method);
}
为了简化我假设我的代理类和代理实现的接口中所定义的方法没有参数,所以和java.lang.reflect.InvocationHandler中的相比缺少方法参数args;
我的Proxy类:
package com.learn.proxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
import com.learn.proxy.test.Moveable;
public class Proxy {
public static Object newProxyInstance(ClassLoader loader, Class interfaces, InvocationHanlder h) throws Exception {
String rt = "\r\n";
String methodStr = "";
Method[] methods = Moveable.class.getMethods();
for(Method m : methods) {
methodStr += " @Override" + rt +
" public " + m.getGenericReturnType() + " " + m.getName() + "()" + " {" + rt +
" try{" + rt +
" Method md = " + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" hanlder.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
" }" + rt;
}
String str =
"package com.learn.proxy; " + rt +
rt +
"import java.lang.reflect.Method;" + rt +
rt +
"public class $Proxy1 implements " + interfaces.getName() +" { " + rt +
" private com.learn.proxy.InvocationHanlder hanlder;" + rt +
" public $Proxy1(InvocationHanlder hanlder) {" + rt +
" this.hanlder = hanlder; " + rt +
" } " + rt +
methodStr +
"} " + rt ;
String fileName = System.getProperty("user.dir") + "/proxy/com/learn/proxy/$Proxy1.java";
File file = new File(fileName);
FileWriter writer = new FileWriter(file);
writer.write(str);
writer.flush();
writer.close();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
task.call();
fileManager.close();
file.delete();
Class c = loader.loadClass("com.learn.proxy.$Proxy1");
Constructor constructor = c.getDeclaredConstructor(InvocationHanlder.class);
Object obj = constructor.newInstance(h);
return obj;
}
}
此类包含一个方法newProxyInstance,此方法可以产生一个代理,此代理要实现的接口由参数interfaces指定,loader用来加载动态生成的代理类,h用来处理被代理类要执行方法;
3.接下来给出一个上面2的应用:
同样有一个Person对象,它实现了Moveable接口,详细代码如下;
public interface Moveable {
public void move();
}
public class Person implements Moveable {
public void move() {
System.out.println("Person remove");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
}
}
}
现在要求记录Person对象执行move方法的时间,我们可以这样处理;
Step 1 分析我们首先需要一个调用处理对象,我们定义它的名字为TimeHanlder,当然这里的TimeHanlder实现我们自己定义的InvocationHanlder,如下:
package com.learn.proxy.test;
import java.lang.reflect.Method;
import java.util.Date;
import com.learn.proxy.InvocationHanlder;
public class TimeHanlder implements InvocationHanlder {
private Object target;
public TimeHanlder(Object target) {
this.target = target;
}
@Override
public void invoke(Object obj, Method method) {
Date start = new Date();
System.out.println("START...");
try {
method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("END, TOTAL APENT: " + (new Date().getTime() - start.getTime()) + " MILLISECONDS.");
}
}
Step 2 产生代理类
有了调用处理对象,我们还需要指定加载我们生成代理类的ClassLoader,根据我们自己的Proxy,我们是将代理类编译好放在一个单独的位置,所以我们不能用程序中的ClassLoader,这里我们用URLClassLoader;
如下代码:
public static void main(String[] args) throws Exception {
URL[] urls = new URL[]{new URL("file://" + System.getProperty("user.dir") + "/proxy/")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
TimeHanlder hanlder = new TimeHanlder(new Person());
Object proxy = Proxy.newProxyInstance(urlClassLoader,Moveable.class,hanlder);
System.out.println(proxy.getClass());
System.out.println(proxy.getClass().getInterfaces()[0]);
System.out.println(proxy.getClass().getConstructors()[0]);
System.out.println(proxy.getClass().getMethods()[0]);
Moveable m = (Moveable) proxy;
m.move();
}
此段代码运行结果如下:
class com.learn.proxy.$Proxy1
interface com.learn.proxy.test.Moveable
public com.learn.proxy.$Proxy1(com.learn.proxy.InvocationHanlder)
public void com.learn.proxy.$Proxy1.move()
START...
Person remove
END, TOTAL APENT: 2562 MILLISECONDS.
Step 3 分析整个运行过程:
从结果看可以知道:
我们的代理类名字为com.learn.proxy.$Proxy1;
它有一个构造方法:public $Proxy1(com.learn.proxy.InvocationHanlder hanlder);
它实现了接口com.learn.proxy.test.Moveable;
它有一个方法public void move() ,显然这方法是com.learn.proxy.test.Moveable中定义的方法
这样我们可以大致还原出我们的代理类代码:
package com.learn.proxy;
import java.lang.reflect.Method;
public class $Proxy1 implements com.learn.proxy.test.Moveable {
private com.learn.proxy.InvocationHanlder hanlder;
public $Proxy1(InvocationHanlder hanlder) {
this.hanlder = hanlder;
}
@Override
public void move() {
try{
Method md = com.learn.proxy.test.Moveable.class.getMethod("move");
hanlder.invoke(this, md);
}catch(Exception e) {e.printStackTrace();}
}
}
此类是程序调运Proxy.newProxyInstance方法时动态生成,实际中并不存在;
接着分析,上述运行结果,Person对象move方法被执行,同时执行move方法的时间被记录,所以整个过程可以用下图描述:
首先动态生成的代理类中move方法被调用;
TimeHanlder中invoke方法被调用;
java.lang.reflect.Method中invoke方法被调用,来完成执行Person中move方法;