HadoopRPC机制分析系列之一: 动态代理

来源:互联网 发布:js改变label的宽度 编辑:程序博客网 时间:2024/06/01 17:24

为了搞明白Hadoop中的RPC机制,按照代理->动态代理->Hadoop中RPC机制的顺序,进行学习。

代理模式  

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 
按照代理的创建时期,代理类可以分为两种。 
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。 

1.      静态代理

举个例子:

public interface A{

    void do();

}

 

public class B implements A{

public void  do()   

{… }  

 

}

public class ProxyC  implements A{  

private  A  proxy;

  public  ProxyC(A  a) {

super();

   proxy = a ;

 

public void  do()  //这里对B进行代理

{… }  //这里可以做你想做的任何事情  

}

 

public class test{

 public static void main(String[] args)  {

   B b = new B();

   A a = new ProxyC(b);

a.do();

}

}

 

通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。 

2.      动态代理的优势

1)      观察代码可以发现每一个代理类只能为一个接口(Interface,比如ProxyC只能为A接口服务)服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

 

2)      动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。 

  

 代码示例:

  public interface A{

    void do();

}

 

public class AImpl  implements A{

public void  do()  

{… }  

}

 

public class InvocationHandlerImpl  implements  InvocationHandler{

  A a ;

 

  PublicInvocationHandlerImpl(A ai)

{

    A =ai;

@Override  

    public Object invoke(Object proxy, Method method, Object[] arg)  

            throws Throwable {  

        //调用之前可以做一些处理   

        method.invoke(ac, arg);  //这行业可以注释,总之,所有对接口的请求都会转发到这里,可以使用invoke调用实现类同名接口,也可以自己处理。

        //调用之后也可以做一些处理   

        return null;  

    }   

}

 

public class ProxyTest 

{

public static void main(String[] args) { 

     AImpl   al = new AImpl ();

     InvocationHandlerImpl  inhandler =new InvocationHandlerImpl(al);

     //将处理类和接口相关联

     

     A a = (A)  Proxy.newProxyInstance(AImpl.getClass().getClassLoader()

, AImpl.getClass().getInterfaces(), inhandler);  

//这里的newProxyInstance函数的工作是通过反射机制生成AImpl的代理类,也就是说,该函数返回一个代理类的实例$ProxyN,并将$ProxyNinhandler相关联,这样,所有对接口A的请求都会转发到inhandlerinvoke函数中,在该函数中既可以使用method.invoke()调用实现类同名接口,也可以自己处理.

      a.do() ;

}

}

 

  综上可见,动态代理的优势是避免一个接口一个代理,代理类可以在执行过程中动态生成。

 

这里可以参考下Proxy的newProxyInstance函数:

public static Object newProxyIntenct(Class infac,InvocationHandler h) throws Exception{

   …

    //通过有参的构造器反射生成代理类的实例  

Constructor ctr = c.getConstructor(InvocationHandler.class);  

                Object obj = (Object) ctr.newInstance(h);   

                return obj;  

   …

}

/**  

这里要解释下Proxy.newProxyInstance(AImpl.getClass().getClassLoader()

, AImpl.getClass().getInterfaces(), inhandler) 的具体工作 

 

一.  AImpl.getClass().getClassLoader():代理类的类加载器

  AImpl.getClass().getInterfaces():代理类所实现的所有的接口

 该函数返回动态生成的代理类$Proxy0的Class对象,且该类$Proxy0实现了要代理类的实现的所有接口,并继承了Proxy接口。  

 

二. 实例化这个动态生成的$Proxy0类的一个实例,实例化代理类的构造函数为Proxy(InvocationHandler h), 也就是说要实例化这个动态生成的$Proxy0类,必须给它一个InvocationHandler参数,也就是我们自己实现的用来在代理类方法执行前后做额外工作的类InvocationHandlerImpl  。 

      这段代码Proxy.newProxyInstance(AImpl.getClass().getClassLoader()

, AImpl.getClass().getInterfaces(), inhandler) 得到的其实是一个类名叫$Proxy0 extends Proxy implements A的类。

 

三. 将这个$Proxy0类强制转型成AImpl类型,调用do方法。

*/  

 

简单的说,客户端用newProxyInstance生成代理实例,强制转换成A接口类型->调用A的do方法-> 调用处理类InvocationHandlerImpl的invoke方法。

 

    这样,就可以基本上理解Java的代理和动态代理的概念,后面会接着分析Hadoop中的RPC机制中动态代理的使用。有兴趣的可以自己深究下java.lang.reflect看看里面的细节。

 

参考资料:

1.      http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

2.      http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

3.      http://gongjiayun.iteye.com/blog/948778

4.      http://hi.baidu.com/malecu/item/9e0edc115cb597a1feded5a0

5.      http://www.iteye.com/topic/683613

 

原创粉丝点击