基于springmvc的hessian调用原理浅析

来源:互联网 发布:女程序员能干到多少岁 编辑:程序博客网 时间:2024/05/22 00:52
一、客户端

1、构造(初始化)

由客户端的配置文件随容器的启动而进行初始化,配置文件如下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context         http://www.springframework.org/schema/context/spring-context.xsd">    <context:property-placeholder location="classpath:/server.properties" ignore-unresolvable="true"/>    <bean id="application"        class="org.springframework.remoting.caucho.HessianProxyFactoryBean">        <property name="serviceUrl">            <value>${app_server_path}</value>        </property>        <property name="serviceInterface">            <value>com.avatarmind.server.service.ApplicationService</value>        </property>    </bean></beans>
View Code

先放张类关系图:

根据spring生命周期可知,容器初始化,会调用InitializingBean的afterPropertiesSet()方法,

而HessianProxyFactoryBean类实现了InitializingBean接口并重写了afterPropertiesSet()方法。

所以主要流程为:

1)、HessianProxyFactoryBean的afterPropertiesSet()

2)、HessianClientInterceptor的afterPropertiesSet()

3)、HessianClientInterceptor的prepare()

4)、HessianClientInterceptor的createHessianProxy(HessianProxyFactory proxyFactory)

5)、HessianProxyFactory的create(Class<?> api, String urlName, ClassLoader loader)

  public Object create(Class<?> api, URL url, ClassLoader loader)  {    if (api == null)      throw new NullPointerException("api must not be null for HessianProxyFactory.create()");    InvocationHandler handler = null;    handler = new HessianProxy(url, this, api);    return Proxy.newProxyInstance(loader,                                  new Class[] { api,                                                HessianRemoteObject.class },                                  handler);  }
View Code

整个流程最终就是为服务接口生成代理类。

2、使用接口调用服务

具体的方法调用我就不写了,不知道的可以看我以前写的hessian的基本使用。

由public class HessianProxy implements InvocationHandler, Serializable和构造的第 5)里的代码可知

接口的调用会转到HessianProxy的invoke(Object proxy, Method method, Object []args)方法里。

具体的代码如下,有些细节我也不是很懂,好像就是序列化、发送请求(conn = sendRequest(mangleName, args);)、

反序列化,还有校验和处理其他方法。

  public Object invoke(Object proxy, Method method, Object []args)    throws Throwable  {    String mangleName;    synchronized (_mangleMap) {      mangleName = _mangleMap.get(method);    }    if (mangleName == null) {      String methodName = method.getName();      Class<?> []params = method.getParameterTypes();      // equals and hashCode are special cased      if (methodName.equals("equals")          && params.length == 1 && params[0].equals(Object.class)) {        Object value = args[0];        if (value == null || ! Proxy.isProxyClass(value.getClass()))          return Boolean.FALSE;        Object proxyHandler = Proxy.getInvocationHandler(value);        if (! (proxyHandler instanceof HessianProxy))          return Boolean.FALSE;        HessianProxy handler = (HessianProxy) proxyHandler;        return new Boolean(_url.equals(handler.getURL()));      }      else if (methodName.equals("hashCode") && params.length == 0)        return new Integer(_url.hashCode());      else if (methodName.equals("getHessianType"))        return proxy.getClass().getInterfaces()[0].getName();      else if (methodName.equals("getHessianURL"))        return _url.toString();      else if (methodName.equals("toString") && params.length == 0)        return "HessianProxy[" + _url + "]";            if (! _factory.isOverloadEnabled())        mangleName = method.getName();      else        mangleName = mangleName(method);      synchronized (_mangleMap) {        _mangleMap.put(method, mangleName);      }    }    InputStream is = null;    HessianConnection conn = null;        try {      if (log.isLoggable(Level.FINER))        log.finer("Hessian[" + _url + "] calling " + mangleName);      // 调用服务端      conn = sendRequest(mangleName, args);      is = getInputStream(conn);      if (log.isLoggable(Level.FINEST)) {        PrintWriter dbg = new PrintWriter(new LogWriter(log));        HessianDebugInputStream dIs          = new HessianDebugInputStream(is, dbg);        dIs.startTop2();        is = dIs;      }      AbstractHessianInput in;      int code = is.read();      if (code == 'H') {        int major = is.read();        int minor = is.read();        in = _factory.getHessian2Input(is);        Object value = in.readReply(method.getReturnType());        return value;      }      else if (code == 'r') {        int major = is.read();        int minor = is.read();        in = _factory.getHessianInput(is);        in.startReplyBody();        Object value = in.readObject(method.getReturnType());        if (value instanceof InputStream) {          value = new ResultInputStream(conn, is, in, (InputStream) value);          is = null;          conn = null;        }        else          in.completeReply();        return value;      }      else        throw new HessianProtocolException("'" + (char) code + "' is an unknown code");    } catch (HessianProtocolException e) {      throw new HessianRuntimeException(e);    } finally {      try {        if (is != null)          is.close();      } catch (Exception e) {        log.log(Level.FINE, e.toString(), e);      }            try {        if (conn != null)          conn.destroy();      } catch (Exception e) {        log.log(Level.FINE, e.toString(), e);      }    }  }
View Code

 二、服务端

1、构造(初始化)

配置文件:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"    xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">        <!-- app -->    <bean id="applicationServiceImpl" class="com.server.service.impl.ApplicationServiceImpl" />        <!-- 使用HessianServiceExporter 将普通bean导出成Hessian服务 -->    <bean name="/app"        class="org.springframework.remoting.caucho.HessianServiceExporter">                <property name="service" ref="applicationServiceImpl" />        <!-- Hessian服务的接口 -->        <property name="serviceInterface" value="com.server.service.ApplicationService" />    </bean>   </beans>
View Code

同理:

主要流程:

1)、HessianExporter的afterPropertiesSet()

2)、HessianExporter的prepare()

3)、RemoteExporter的getProxyForService()

流程主要是生成HessianSkeleton类的对象。

因为配置文件的bean的name带 / 符号,所以会被BeanNameUrlHandlerMapping进行映射处理。

2、被调用

由于基于mvc,所以请求会先到DispatcherServlet的doDispatch方法,然后根据路径获取handler,

因此服务的入口为HessianServiceExporter的handleRequest()方法。

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {                // 验证服务是否启动成功的地方        if (!"POST".equals(request.getMethod())) {            throw new HttpRequestMethodNotSupportedException(request.getMethod(),                    new String[] {"POST"}, "HessianServiceExporter only supports POST requests");        }        response.setContentType(CONTENT_TYPE_HESSIAN);        try {                  // 调用          invoke(request.getInputStream(), response.getOutputStream());        }        catch (Throwable ex) {          throw new NestedServletException("Hessian skeleton invocation failed", ex);        }    }                      
View Code

流程:

1)、HessianServiceExporter的handleRequest()

2)、HessianExporter的invoke(InputStream inputStream, OutputStream outputStream)

3)、HessianExporter的doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)

核心代码:skeleton.invoke(in, out);

4)、HessianSkeleton的invoke()

核心代码:result = method.invoke(service, values);

利用反射调用具体的实现方法。中间掺杂着反序列化,校验,序列化的其他方法。

有些地方自己也不是很懂,主要是记录大体的流程,细节后续再细究吧。

原创粉丝点击