您的位置:首页 > 编程语言 > Java开发

JDK动态代理的简单实现

2016-04-27 00:00 411 查看
摘要: 调用 Proxy 实例的方法时, 都会被 InvocationHandler 实例对象的 invoke() 方法所捕获.
这是如何做到的呢?
下面将从源码的角度解答

1. 先理一下动态代理实现的思路:

实现功能: 自己定义一个类 Proxy, 通过Proxy的静态方法 newProxyInstance(Class<T> intface,InvocationHandler h)返回代理对象, intface: 被代理类的接口对象, h: InvocationHandler的实例对象

1). 声明一段动态代理类的源码( 动态产生代理类 )

2). 编译动态代理类的源码( JDK Compiler API ), 产生代理类

3). 通过 ClassLoader 加载这个代理类, 创建一个代理类的实例对象

4). return 返回这个代理对象

2. 代码实现:

为什么代理类的类名为 $Proxy0?

这是因为 Java中动态代理, 生成的代理类的类名就是 $Proxy0, 依葫芦画瓢而已,

可以用一个动态代理对象 proxy, 来验证:

System.out.println(proxy.getClass().getName()); //输出com.sun.proxy.$Proxy0

[code=plain]public class Proxy {
/**
* @param intface
*            被代理类的接口的类对象
* @param h
*            InvocationHandler的实例对象
* @return proxy 生成的动态代理对象
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T newProxyInstance(Class<T> intface,InvocationHandler h) throws Exception {
String srcStr = "";    // 代理类$Proxy0的源码, 字符串形式
String methodStr = "";    // 代理类$Proxy0的所有代理方法, 字符串形式
String rt  = "\r\n";    // Windows平台下的换行符

// 动态生成代理类$Proxy0的所有代理方法
for(Method m : intface.getMethods()) {
methodStr +=
"	@Override" + rt +
"	public void " + m.getName() + "() {" + rt +
"		try {" + rt +
"			Method method = " + intface.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
"			h.invoke(this, method, null);" + rt +	// 暂时只支持无参方法
"		} catch (Throwable e) { e.printStackTrace(); }" + rt +
"	}";
}

// 拼接代理类$Proxy0的源码
srcStr +=
"package proxy.my;" + rt +
"import java.lang.reflect.Method;" + rt +
"import java.lang.reflect.InvocationHandler;" + rt +
"public class $Proxy0 implements " + intface.getName() + "{" + rt +
"	private InvocationHandler h;" + rt +
"	public $Proxy0(InvocationHandler h) {" + rt +
"		this.h = h;" + rt +
"	}" + rt +
"  "+methodStr + rt +
"}";

// 生成代理类的java源文件
String srcFilePath = System.getProperty("user.dir") + "/bin/proxy/my/$Proxy0.java";
File srcFile = new File(srcFilePath);
// 使用commons-io-2.2.jar中的FileUtils, 向一个指定的文件中写入指定的字符串
FileUtils.writeStringToFile(srcFile, srcStr);

// 编译这个代理类的java源文件
// 获取编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 获取文件管理者
StandardJavaFileManager fileMgr= compiler.getStandardFileManager(null, null, null);
// 获取文件
Iterable<? extends JavaFileObject> compilationUnits = fileMgr.getJavaFileObjects(srcFilePath);
// 获取编译任务
CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, compilationUnits);
// 编译
task.call();

// 获取代理类的类加载器
ClassLoader classLoader = Proxy.class.getClassLoader();
// 加载代理类
Class<?> clazz = classLoader.loadClass("proxy.my.$Proxy0");
// 获取代理类的构造器
Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class);
// 通过代理类的构造器, 创建一个代理类的实例, 也就是代理对象, 返回代理对象
T proxy = (T) constructor.newInstance(h);
return proxy;
}
}


3. 测试自己实现的 Proxy.newProxyInstance() 方法

先定义一个 ProxyInvocationHandler 类, 该类实现了java.lang.reflect.InvocationHandler接口, 实现invoke()方法

[code=plain]public class ProxyInvocationHandler implements InvocationHandler {
private Object target;    // 被代理的目标对象

public ProxyInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("来不及解释了, 快上车");
method.invoke(target, args);    // 调用目标对象的方法
System.out.println("下车了, 快记住车牌号");
return null;    // 暂时只支持无参方法
}
}


测试动态代理

[code=plain]public static void main(String[] args) throws Throwable {
Moveable car = new Car();
InvocationHandler h = new ProxyInvocationHandler(car);
Moveable carProxy = Proxy.newProxyInstance(Moveable.class, h);
carProxy.move();
}

Console输出: // 其实我想当个老司机, 天天飙车

来不及解释了, 快上车

飙车中...

下车了, 快记住车牌号

Moveable 接口

[code=plain]public interface Moveable {
public void move();
}

Moveable 接口的实现类 Car

[code=plain]public class Car implements Moveable {
@Override
public void move() {
System.out.println("飙车中...");
}
}

生成的代理类 $Proxy0, 其路径: bin/proxy/$Proxy0.java

[code=plain]package proxy;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationHandler;
public class $Proxy0 implements proxy.Moveable{
private InvocationHandler h;
public $Proxy0(InvocationHandler h) {
this.h = h;
}
@Override
public void move() {
try {
Method method = proxy.Moveable.class.getMethod("move");
h.invoke(this, method, null);    // 暂时只支持无参方法
} catch (Throwable e) { e.printStackTrace(); }
}
}


总结:

调用 Proxy 实例的方法时, 都会被 InvocationHandler 的实例对象 invoke() 方法所捕获

why?

生成的动态类 $Proxy0, 重写了被代理类 Car 的 move() 方法, 在 move() 方法里, 都是在调用InvocationHandler 实例对象的 invoke() 方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息