java动态代理原理

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): 
             ==>每一个方法的调用就是:
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真正执行。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章