Hessian解析及应用(整合Spring)

来源:互联网 发布:qq电脑管家网络加速器 编辑:程序博客网 时间:2024/06/01 07:56

  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的,就怎么玩吧。
测试内容
复制代码
@Controllerpublic 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     else100       in.completeReply();101 102     return value;103       }104       else105     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 first10     in.skipOptionalCall();11 12     // Hessian 1.0 backward compatibility13     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 object71       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 a91     // trailing InputStream92     in.completeCall();93 94     out.writeReply(result);95 96     out.close();97   }
复制代码
    关键在于输入输出流的序列化,这部分内容暂未了解......
0 0
原创粉丝点击