java的动态代理学习
此前总是在hadoop的学习中听说这个名词,但是总是不知道其所以然。今天有空来好好的研究研究。
所有的编程代码学习都离不开示例,尤其是自己编写代码。下面是一个示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* 理解理解java的代理机制(proxy) <br>
* 1.代理机制的现实版演绎: <br>
* 就拿买火车票来说,我、黄牛、火车代售站;这里有三个人,我是需要买票的客户,我可以直接在“火车代售站”买票,也可以找“黄牛”买票,也就是说
* “黄牛”可以代理火车代售站。即我和黄牛交易与我和火车代售站交易一样,黄牛代理了火车代售站:都有一个卖票的窗口 <br>
* 2.java的代理机制 <br>
* B类持有A类的引用,调用A类就相当于当用B类(好绕口)
*
* @author fangshun.liao
* @version 2015年1月12日 下午2:41:52
*/
public class ProxyTest {
public static void main(String args[]) {
Real real = new Real();
RealInterface proxy = (RealInterface) Proxy.newProxyInstance(real.getClass()
.getClassLoader(), new Class[]{RealInterface.class}, new InvocationReal(real));
proxy.print();
}
}
interface RealInterface{
public void print();
}
class Real implements RealInterface{
public void print() {
System.out.println("do Real");
}
}
class InvocationReal implements InvocationHandler {
private Real real;
public InvocationReal( Real real) {
this.real = real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("proxy invocation starting...");
method.invoke(real, args);
System.out.println(method.getName());
System.out.println("proxy invocation ended");
return proxy;
}
}
上面的代码是一个非常简单的示例,就是将本来new好的对象再用Proxy包装起来:
RealInterface proxy = (RealInterface) Proxy.newProxyInstance(real.getClass() .getClassLoader(),
new Class[]{RealInterface.class}, new InvocationReal(real));
这个方法看起来好怪啊,classLoader都出来了,且第二个必须是interface才行,第三个也不能为空,且又引用了real。当看到这时会发现,这样子写程序真是脑子有毛病呢!明明就有个real了,干脆直接调用real就好了,还要绕来绕去干什么?是的,如果就光这个例子,是不能够说明什么问题的,且反而感觉憋手蹩脚的。不妨先不考虑动态代理到底有什么用,而是猜猜它的实现方式即执行方式:
1.参数说明及用途猜想
classLoader:其可以解析出java字节码(.class)文件,那么如果我们能够生成java的字节码,我们就可以用此加载出一个类;更进一步:在知道所有方法名时就可以自动生成一个类。这里猜测newProxyInstance方法会自己创造一个类,这个类实现了interfaces。这样解释有点说的通此方法返回值必须是interfaces中的一个,而不能是类(如
Real类)。
InvocationHandler:方法调用的句柄,也就是proxy调用方法后最终真正实现的地方。且看其实现方式:
class InvocationReal implements InvocationHandler {
private Real real;//其实这个InvocationReal 和Real可以没有任何关系
public InvocationReal( Real real) {
this.real = real;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)//proxy为方法所依赖的代理,method即调用的方法实体类,args就是参数列表
throws Throwable {
System.out.println("proxy invocation starting...");
method.invoke(real, args);//这里真正执行方法
System.out.println(method.getName());
System.out.println("proxy invocation ended");
return proxy;
}
}
2.proxy.print()的调用过程猜想
上面获得了一个proxy,下面就可以来调用方法了:proxy.print();
要想知道此方法最终是怎么以一个进行过程,可以debug跟踪。会发现其是直接跳入到了InvocationReal .invoke(...)方法中的。这里如果再和上面的猜测结合,这里我们可以自己动手写一个将就的动态代理的实现了,下面将我自己要实现的方法的伪代码给出:
Proxy.newProxyInstance(real.getClass() .getClassLoader(),
new Class[]{RealInterface.class}, new InvocationReal(real)):
1.Object [] its = getAllInterfaces();//获取所有的接口
2. Object cl = generateClassFromInterfaces(its,InvocationHandler);//根据interfaces生成一个类,这个类实现了所有的接口
3. generateClassFromInterfaces(its):
3. generateClassFromInterfaces(its):
==>每一个方法的调用就是:
try {
Method method = this.getClass().getMethod("print", new Class[]{});
Object args[] = new Object[1];
args[0] = null;//因为此处没有输入参数
InvocationReal.invoke(this,method,args);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
实验后发现proxy的名字就是com.xxx.xxx.xxx_test.$Proxy0,看来猜测是猜对了。
原理猜测出来,那么什么地方用到了呢?这个代理其实在远程过程调用(RPC)见的最多,不妨思考思考,在newProxyInstance时我们只要接口就行了,具体怎么实现的都可以不用管,然后在invocationHandler的invoke中我们可以做非常多的事情,如将method序列化发送出去,然后等待远处返回结果等等。即我们自己写一个invocationHandler就行,至于invoke中怎么写就随便你了。这样看来是不是感觉非常牛叉?
其实代理模式就是A持有B的引用,调用A的方法全部由B真正执行。