zoukankan      html  css  js  c++  java
  • Hessian解析及应用(整合Spring)

            Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。------来自百度百科。

            看官网简介吧:http://hessian.caucho.com/index.xtp

            The Hessian binary web service protocol makes web services usable without requiring a large framework, and without learning yet another alphabet soup of protocols. Because it is a binary protocol, it is well-suited to sending binary data without any need to extend the protocol with attachments.

            此文使用hessian为hessian-4.0.7,源码及jar包请自行下载。------当然是java版了,咱也不会别的。

            开搞吧!

            除了提供下载,Hessian官网上貌似就下面几句话有用了(可能是Hessian使用起来很简单的原因吧)。

            开搞吧,两个应用,Client(hessianclient)调用Server(hessianserver)提供的服务并展示返回内容。

            创建公共接口(单独打包成Jar,方便多个项目使用)。

    package com.skyjoo.hessiancommon.interfaces;
    
    public interface HessianExampleInterface {
        public String hello(String name);
    }

            就一个hello方法,返回String......

            在Server中提供该接口的实现。

    package com.skyjoo.hessianserver.interfacesImpl;
    
    import com.skyjoo.hessiancommon.interfaces.HessianExampleInterface;
    
    
    public class HessianExampleInterfaceImpl implements HessianExampleInterface {
    
        @Override
        public String hello(String name) {
            return "Hello " + name + ",Welcome to Hessian World!!!";
        }
    
    }

            像上面这样肯定是没法工作的,只要有WebService经验或了解过Axis的类似开源框架的童鞋肯定都知道关键的是发布服务......

            介绍中的"轻量级"说明发布服务应该是很简单的......

            本文结合Spring,看一下配置吧。

    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
            <property name="order" value="100" />
            <property name="urlMap">
                <map>
                    <entry key="/remote/hessianService.htm" value-ref="exampleService" />
                </map>
            </property>
    </bean>
    <bean id="exampleService"
            class="org.springframework.remoting.caucho.HessianServiceExporter">
            <property name="service" ref="hessianExampleService" />
            <property name="serviceInterface"
                value="com.skyjoo.hessiancommon.interfaces.HessianExampleInterface" />
    </bean>
    <bean id="hessianExampleService"
            class="com.skyjoo.hessianserver.interfacesImpl.HessianExampleInterfaceImpl" />

        

        <entry key="/remote/hessianService.htm" value-ref="exampleService" />
        将某个地址对应到Bean上,这里的Bean为org.springframework.remoting.caucho.HessianServiceExporter。该Bean的service属性即服务端对公共接口的实现,serviceInterface即公共接口。还是很容易就能理解的。
    接着就是Client的内容。
        <bean id="hessianService"
            class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
            <property name="serviceUrl" value="http://localhost:8090/hessianserver/remote/hessianService.htm" />
            <property name="serviceInterface"
                value="com.skyjoo.hessiancommon.interfaces.HessianExampleInterface" />
        </bean>

            使用org.springframework.remoting.caucho.HessianProxyFactoryBean,serviceUrl为服务地址,serviceInterface为公共接口。看serviceUrl的内容

        http://localhost:8090/hessianserver/remote/hessianService.htm

    前半部分是服务部署地址,后半部分为<entry key="/remote/hessianService.htm" value-ref="exampleService"/>配置中指定的key。

    然后......然后就没有了,Spring怎么装配Bean的,平时怎么使用Bean的,就怎么玩吧。
    测试内容
    @Controller
    public class Test {
        @Autowired
        private HessianExampleInterface hessianService;
    
        @RequestMapping("/hello")
        public void hello(@RequestParam("username") String username, ModelMap model) {
            model.put("helloUser", hessianService.hello(username));
        }
    }

            和使用普通的Bean一模一样。

    (这图上的好无聊~~~~~~~~~~~)

        只是简单的实现了一个例子,还没能深入的学习、理解hessian的内容。
    --------------------------------------------------------------------华丽丽的分割线---------------------------------------------------------------

    未完待续......

    昨天只是简单的例子,现在续上......
    Hessian在Spring中的工作流程如下:
    (1)客户端:
    a.发送远程请求:
    客户端程序-->调用公共接口的方法-->Hessian拦截器拦截请求-->封装远程调用请求-->Hessian代理-->通过HTTP协议发送远程请求代理到服务端
    b.接收远程调用响应:
    远程调用结果-->HTTP响应-->客户端
    (2)服务端:
    a.接收远程调用请求:
    HessianServiceExporter接收请求-->将远程调用对象封装为HessianSkeleton框架-->
    HessianSkeleton处理远程调用请求
    b.返回远程调用响应:
    HessianSkeleton封装远程调用处理结果-->HTTP响应-->客户端
    下图是通过hessian一次完成调用的示意图


    在上面的例子中,Spring通过HessianProxyFactoryBean管理Hessian客户端。
    HessianProxyFactoryBean.java
    public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
        //远程调用代理对象
        private Object serviceProxy;
        //Spring IoC依赖注入完成后的回调方法
        @Override
        public void afterPropertiesSet() {
            super.afterPropertiesSet();
            //设置远程代理对象,getServiceInterface()即获取配置中的serviceInterface(公共接口)
            //HessianProxyFactoryBean 继HessianClientInterceptor ,本身就是一个拦截器,所以传入拦截器为this
            this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
        }
    
        //返回远程调用代理对象
        public Object getObject() {
            return this.serviceProxy;
        }
        //获取对象类型(即公共接口)
        public Class<?> getObjectType() {
            return getServiceInterface();
        }
        //判断是否是单例,Spring默认管理对象都是单例的
        public boolean isSingleton() {
            return true;
        }
    
    }
        HessianProxyFactoryBean最核心的功能是在IoC容器回调方法中产生远程调用代理对象,将代理对象的拦截器设置为HessianClientInterceptor。
      1 public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {  
      2     //创建Hessiann代理工厂  
      3     private HessianProxyFactory proxyFactory = new HessianProxyFactory();  
      4     //Hessian代理  
      5     private Object hessianProxy;  
      6     //设置Hessian代理工厂  
      7     public void setProxyFactory(HessianProxyFactory proxyFactory) {  
      8         this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());  
      9     }  
     10     //设置Hessian序列化工厂  
     11     public void setSerializerFactory(SerializerFactory serializerFactory) {  
     12         this.proxyFactory.setSerializerFactory(serializerFactory);  
     13     }  
     14     //设置Hessian是否发送java集合类型对象  
     15     public void setSendCollectionType(boolean sendCollectionType) {  
     16     this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);  
     17     }  
     18     //设置远程调用时是否重载方法  
     19     public void setOverloadEnabled(boolean overloadEnabled) {  
     20         this.proxyFactory.setOverloadEnabled(overloadEnabled);  
     21     }  
     22     //设置远程调用用户名  
     23     public void setUsername(String username) {  
     24         this.proxyFactory.setUser(username);  
     25     }  
     26     //设置远程调用密码  
     27     public void setPassword(String password) {  
     28         this.proxyFactory.setPassword(password);  
     29     }  
     30     //设置是否使用Hessian的Debug调试模式  
     31     public void setDebug(boolean debug) {  
     32         this.proxyFactory.setDebug(debug);  
     33     }  
     34     //设置是否使用chunked端口发送Hessian请求  
     35     public void setChunkedPost(boolean chunkedPost) {  
     36         this.proxyFactory.setChunkedPost(chunkedPost);  
     37     }  
     38     //设置Hessian等待响应的超时时长  
     39     public void setReadTimeout(long timeout) {  
     40         this.proxyFactory.setReadTimeout(timeout);  
     41     }  
     42     //设置是否使用Hessain版本2协议解析请求和响应  
     43     public void setHessian2(boolean hessian2) {  
     44         this.proxyFactory.setHessian2Request(hessian2);  
     45         this.proxyFactory.setHessian2Reply(hessian2);  
     46     }  
     47     //设置是否使用Hessian版本2协议解析请求  
     48     public void setHessian2Request(boolean hessian2) {  
     49         this.proxyFactory.setHessian2Request(hessian2);  
     50     }  
     51     //设置是否使用Hessian版本2协议解析响应  
     52     public void setHessian2Reply(boolean hessian2) {  
     53         this.proxyFactory.setHessian2Reply(hessian2);  
     54     }  
     55 //子类HessianProxyFactoryBean的回调方法调用此回调方法  
     56     public void afterPropertiesSet() {  
     57         //调用其父类UrlBasedRemoteAccessor的回调方法获取客户端配置的请求url  
     58         super.afterPropertiesSet();  
     59         //初始化Hessian代理  
     60         prepare();  
     61     }  
     62     //初始化Hessian代理  
     63     public void prepare() throws RemoteLookupFailureException {  
     64         try {  
     65             //创建Hessian代理  
     66             this.hessianProxy = createHessianProxy(this.proxyFactory);  
     67         }  
     68         catch (MalformedURLException ex) {  
     69             throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);  
     70         }  
     71     }  
     72     //创建Hessian代理  
     73     protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {  
     74         Assert.notNull(getServiceInterface(), "'serviceInterface' is required");  
     75         //使用Hessian代理工厂创建Hessian代理  
     76         return proxyFactory.create(getServiceInterface(), getServiceUrl());  
     77     }  
     78     //拦截器客户端请求的方法  
     79     public Object invoke(MethodInvocation invocation) throws Throwable {  
     80         if (this.hessianProxy == null) {  
     81             throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +  
     82                     "invoke 'prepare' before attempting any operations");  
     83         }  
     84         //获取当前环境中线程类加载器  
     85         ClassLoader originalClassLoader = overrideThreadContextClassLoader();  
     86         try {  
     87         //调用Hessian代理的方法,是Hessian远程调用的入口方法,使用JDK反射机制  
     88             return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());  
     89         }  
     90         //处理Hessian远程调用中的异常  
     91         catch (InvocationTargetException ex) {  
     92             Throwable targetEx = ex.getTargetException();  
     93             if (targetEx instanceof InvocationTargetException) {  
     94                 targetEx = ((InvocationTargetException) targetEx).getTargetException();  
     95             }  
     96             if (targetEx instanceof HessianConnectionException) {  
     97                 throw convertHessianAccessException(targetEx);  
     98             }  
     99             else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {  
    100                 Throwable cause = targetEx.getCause();  
    101                 throw convertHessianAccessException(cause != null ? cause : targetEx);  
    102             }  
    103             else if (targetEx instanceof UndeclaredThrowableException) {  
    104                 UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;  
    105                 throw convertHessianAccessException(utex.getUndeclaredThrowable());  
    106             }  
    107             else {  
    108                 throw targetEx;  
    109             }  
    110         }  
    111         catch (Throwable ex) {  
    112             throw new RemoteProxyFailureException(  
    113                     "Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);  
    114         }  
    115         //重置类加载器  
    116         finally {  
    117             resetThreadContextClassLoader(originalClassLoader);  
    118         }  
    119     }  
    120     //将Hessian异常转换为Spring远程调用异常  
    121     protected RemoteAccessException convertHessianAccessException(Throwable ex) {  
    122         if (ex instanceof HessianConnectionException || ex instanceof ConnectException) {  
    123             return new RemoteConnectFailureException(  
    124                     "Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);  
    125         }  
    126         else {  
    127             return new RemoteAccessException(  
    128                 "Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);  
    129         }  
    130     }  
    131 }  
        上面源码中最重要的方法是对远程调用拦截的方法invoke,在该方法中使用JDK的反射机制调用Hessian代理对象的指定方法。而Hessian代理是由Hessain代理器工厂HessianProxyFactory产生的,这个Hessian代理器工厂是有Hessian提供的。
    接着看Hessian的具体实现,这块已经和Spring没有关系了。
    HessianProxy是hessian client处理客户端请求的核心类,采用proxy模式,代理客户端对远程接口的调用,hessian client的主程序的时序图如下:

        HessianProxy在invoke方法中实现对调用方法数参数的序列化为预定格式的输出流,调用sendRequest发送请求,获取返回输入流。
    HessianProxy.java
      1 public Object invoke(Object proxy, Method method, Object []args)
      2     throws Throwable
      3   {
      4     String mangleName;
      5 
      6     synchronized (_mangleMap) {
      7       mangleName = _mangleMap.get(method);
      8     }
      9 
     10     if (mangleName == null) {
     11       String methodName = method.getName();
     12       Class<?> []params = method.getParameterTypes();
     13 
     14       // equals and hashCode are special cased
     15       if (methodName.equals("equals")
     16       && params.length == 1 && params[0].equals(Object.class)) {
     17     Object value = args[0];
     18     if (value == null || ! Proxy.isProxyClass(value.getClass()))
     19       return Boolean.FALSE;
     20 
     21     Object proxyHandler = Proxy.getInvocationHandler(value);
     22 
     23     if (! (proxyHandler instanceof HessianProxy))
     24       return Boolean.FALSE;
     25     
     26     HessianProxy handler = (HessianProxy) proxyHandler;
     27 
     28     return new Boolean(_url.equals(handler.getURL()));
     29       }
     30       else if (methodName.equals("hashCode") && params.length == 0)
     31     return new Integer(_url.hashCode());
     32       else if (methodName.equals("getHessianType"))
     33     return proxy.getClass().getInterfaces()[0].getName();
     34       else if (methodName.equals("getHessianURL"))
     35     return _url.toString();
     36       else if (methodName.equals("toString") && params.length == 0)
     37     return "HessianProxy[" + _url + "]";
     38       
     39       if (! _factory.isOverloadEnabled())
     40     mangleName = method.getName();
     41       else
     42         mangleName = mangleName(method);
     43 
     44       synchronized (_mangleMap) {
     45     _mangleMap.put(method, mangleName);
     46       }
     47     }
     48 
     49     InputStream is = null;
     50     HessianConnection conn = null;
     51     
     52     try {
     53       if (log.isLoggable(Level.FINER))
     54     log.finer("Hessian[" + _url + "] calling " + mangleName);
     55       
     56       conn = sendRequest(mangleName, args);
     57 
     58       is = conn.getInputStream();
     59 
     60       if (log.isLoggable(Level.FINEST)) {
     61     PrintWriter dbg = new PrintWriter(new LogWriter(log));
     62     HessianDebugInputStream dIs
     63       = new HessianDebugInputStream(is, dbg);
     64 
     65     dIs.startTop2();
     66     
     67     is = dIs;
     68       }
     69 
     70       AbstractHessianInput in;
     71 
     72       int code = is.read();
     73 
     74       if (code == 'H') {
     75     int major = is.read();
     76     int minor = is.read();
     77 
     78     in = _factory.getHessian2Input(is);
     79 
     80     Object value = in.readReply(method.getReturnType());
     81 
     82     return value;
     83       }
     84       else if (code == 'r') {
     85     int major = is.read();
     86     int minor = is.read();
     87     
     88     in = _factory.getHessianInput(is);
     89 
     90     in.startReplyBody();
     91 
     92     Object value = in.readObject(method.getReturnType());
     93 
     94     if (value instanceof InputStream) {
     95       value = new ResultInputStream(conn, is, in, (InputStream) value);
     96       is = null;
     97       conn = null;
     98     }
     99     else
    100       in.completeReply();
    101 
    102     return value;
    103       }
    104       else
    105     throw new HessianProtocolException("'" + (char) code + "' is an unknown code");
    106     } catch (HessianProtocolException e) {
    107       throw new HessianRuntimeException(e);
    108     } finally {
    109       try {
    110     if (is != null)
    111       is.close();
    112       } catch (Exception e) {
    113     log.log(Level.FINE, e.toString(), e);
    114       }
    115       
    116       try {
    117     if (conn != null)
    118       conn.destroy();
    119       } catch (Exception e) {
    120     log.log(Level.FINE, e.toString(), e);
    121       }
    122     }
    123   }
        1~47行主要是把methodName缓存起来和过滤一些特殊调用,java反射是个比较耗性能的操作,把methodName缓存起来可以避免每次调用都要从method里得到methodName。另外,对equals、hashCode、getHessianType、getHessianURL等特殊方法的远程调用,直接在本地处理。56行调用了sendRequest方法,在sendRequest方法中初始化连接,调用AbstractHessianOutput包装网络输出流,通过AbstractHessianOutput.call(methodName, args)完成网络输出。
    到这里client先告一段落吧,网络怎么输出的等等没去看......


    Server端通过HessianServiceExporter处理远程调用请求。HessianServiceExporter实现了HttpRequestHandler接口,通过handleRequest方法处理请求。
     1 public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {
     2 
     3     /**
     4      * Processes the incoming Hessian request and creates a Hessian response.
     5      */
     6     public void handleRequest(HttpServletRequest request, HttpServletResponse response)
     7             throws ServletException, IOException {
     8 
     9         if (!"POST".equals(request.getMethod())) {
    10             throw new HttpRequestMethodNotSupportedException(request.getMethod(),
    11                     new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
    12         }
    13 
    14         response.setContentType(CONTENT_TYPE_HESSIAN);
    15         try {
    16           invoke(request.getInputStream(), response.getOutputStream());
    17         }
    18         catch (Throwable ex) {
    19           throw new NestedServletException("Hessian skeleton invocation failed", ex);
    20         }
    21     }
    22 
    23 }
        HessianServiceExporter继承了HessianExporter,调用父类的invoke处理输入流。在HessianExporter的invoke中调用了HessianSkeleton.invoke(InputStream is, OutputStream os)。
    HessianSkeleton是Hessian server端的核心类,主要功能是接收网络输入流(被包装为AbstractHessianInput),反序列化输入流得到methodName和参数,然后根据配置的service,通过反射调用实现类的方法,得到结果后序列化为输出流返回给客户端,流程如下:

        
        HessianSkeleton核心的反射调用非常清晰
     1 public void invoke(Object service,
     2                      AbstractHessianInput in,
     3                      AbstractHessianOutput out)
     4     throws Exception
     5   {
     6     ServiceContext context = ServiceContext.getContext();
     7 
     8     // backward compatibility for some frameworks that don't read
     9     // the call type first
    10     in.skipOptionalCall();
    11 
    12     // Hessian 1.0 backward compatibility
    13     String header;
    14     while ((header = in.readHeader()) != null) {
    15       Object value = in.readObject();
    16 
    17       context.addHeader(header, value);
    18     }
    19 
    20     String methodName = in.readMethod();
    21     int argLength = in.readMethodArgLength();
    22 
    23     Method method;
    24 
    25     method = getMethod(methodName + "__" + argLength);
    26 
    27     if (method == null)
    28       method = getMethod(methodName);
    29 
    30     if (method != null) {
    31     }
    32     else if ("_hessian_getAttribute".equals(methodName)) {
    33       String attrName = in.readString();
    34       in.completeCall();
    35 
    36       String value = null;
    37 
    38       if ("java.api.class".equals(attrName))
    39         value = getAPIClassName();
    40       else if ("java.home.class".equals(attrName))
    41         value = getHomeClassName();
    42       else if ("java.object.class".equals(attrName))
    43         value = getObjectClassName();
    44 
    45       out.writeReply(value);
    46       out.close();
    47       return;
    48     }
    49     else if (method == null) {
    50       out.writeFault("NoSuchMethodException",
    51                      "The service has no method named: " + in.getMethod(),
    52                      null);
    53       out.close();
    54       return;
    55     }
    56 
    57     Class<?> []args = method.getParameterTypes();
    58 
    59     if (argLength != args.length && argLength >= 0) {
    60       out.writeFault("NoSuchMethod",
    61                      "method " + method + " argument length mismatch, received length=" + argLength,
    62                      null);
    63       out.close();
    64       return;
    65     }
    66 
    67     Object []values = new Object[args.length];
    68 
    69     for (int i = 0; i < args.length; i++) {
    70       // XXX: needs Marshal object
    71       values[i] = in.readObject(args[i]);
    72     }
    73 
    74     Object result = null;
    75 
    76     try {
    77       result = method.invoke(service, values);
    78     } catch (Exception e) {
    79       Throwable e1 = e;
    80       if (e1 instanceof InvocationTargetException)
    81         e1 = ((InvocationTargetException) e).getTargetException();
    82 
    83       log.log(Level.FINE, this + " " + e1.toString(), e1);
    84 
    85       out.writeFault("ServiceException", e1.getMessage(), e1);
    86       out.close();
    87       return;
    88     }
    89 
    90     // The complete call needs to be after the invoke to handle a
    91     // trailing InputStream
    92     in.completeCall();
    93 
    94     out.writeReply(result);
    95 
    96     out.close();
    97   }
        关键在于输入输出流的序列化,这部分内容暂未了解......
  • 相关阅读:
    Python绘图与可视化
    ArcGIS Python人门到精通目录基于ArcGIS10.2,100以上案例15章42个视频806分钟,51GIS网站上线
    arcpy 重分类
    pythonw.exe不能用
    Pyhton 单行、多行注释符号使用方法及规范
    NumPyArray
    python 日期
    solr多core的处理
    如何在Solr中实现多core查询
    solr之高级查询--联表 join查询
  • 原文地址:https://www.cnblogs.com/hzmark/p/Hessian.html
Copyright © 2011-2022 走看看