本篇博客我们来看一下Hessian的server端是如何实现的,首先来看一下Server端Hessian的配置
下面是web.xml中的一段配置(web.xml文件是web应用特有的文件,Hessian的server端需要集成在web应用下,如果想在非web应用中使用Hessian的server端需要在Hessian的基础上进行二次开发),servlet和servlet-mapping的映射关系是通过servlet-name对应起来的,/servive/remote路径下的Http请求会映射到HessianServlet上,这些是servlet规范,凡是支持servlet规范的web容器都要符合这样的规范(例如tomcat),但是每个具体的容器的实现可以不同,在规范中每个自定义的servlet一定要实现Servlet接口并实现doGet和doPost方法才可以
但是我们知道servet端的Hessian的并不需要实现Servlet接口,那这又是为什么呢?
因为Hessian替我们做了这些事情,Hessian把servlet相关的细节封装了起来,下面来看一下Hessian是如何封装的
以下面的/service/remote 为例,这个路径下面的Http请求会对应到HessianServlet中,第一次请求会执行HessianServlet的init()方法
<servlet>
<servlet-name>remote-service</servlet-name>
<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
<init-param>
<param-name>home-class</param-name>
<param-value>com.kong.serviceimpl.HessianHelloWorldImpl</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>remote-service</servlet-name>
<url-pattern>/service/remote</url-pattern>
</servlet-mapping>
HessianServlet的init()方法较长,我们只看一下重点的代码
if (_homeImpl != null) { } //getInitParameter()方法是用来获取web.xml配置中servlet标签下init-param标签中的内容,这是servlet-api.jar里面提供的一个方法 else if (getInitParameter("home-class") != null) { String className = getInitParameter("home-class"); //加载<param-value>标签中的类到内存中 Class homeClass = loadClass(className);//代码2 //创建该类的实例 _homeImpl = homeClass.newInstance();//代码3 //如果我们自定义的类实现了Servlet接口或者Service接口(这两个接口都是servlet-api中的接口),那么调用类的init()方法 init(_homeImpl);//代码4 } //service-class和home-class的功能是完全一样的 else if (getInitParameter("service-class") != null) { String className = getInitParameter("service-class"); Class homeClass = loadClass(className); _homeImpl = homeClass.newInstance(); init(_homeImpl); } else { if (getClass().equals(HessianServlet.class)) throw new ServletException("server must extend HessianServlet"); _homeImpl = this; } if (_homeAPI != null) { } else if (getInitParameter("home-api") != null) { String className = getInitParameter("home-api"); _homeAPI = loadClass(className); } else if (getInitParameter("api-class") != null) { String className = getInitParameter("api-class"); _homeAPI = loadClass(className); } else if (_homeImpl != null) { //findRemoteAPI()方法负责找到_homeImpl实现的接口, _homeAPI = findRemoteAPI(_homeImpl.getClass()); if (_homeAPI == null) _homeAPI = _homeImpl.getClass(); }
if (_objectImpl != null) { } else if (getInitParameter("object-class") != null) { String className = getInitParameter("object-class"); Class objectClass = loadClass(className); _objectImpl = objectClass.newInstance(); init(_objectImpl); } if (_objectAPI != null) { } else if (getInitParameter("object-api") != null) { String className = getInitParameter("object-api"); _objectAPI = loadClass(className); } else if (_objectImpl != null) _objectAPI = _objectImpl.getClass(); //创建HessianSkeleton实例,该实例用来处理对应的HTTP请求,并解析请求将请求打到对应的类上 _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI);
HessianServlet的init()方法只会在第一次初始化时执行一次,执行完init()方法后会执行HessianServlet的service()方法而service()方法会调用HessianServlet的HessianSkeleton的invoke()方法,HessianSkeleton对象是在执行HessianServlet的init()方法时创建的
invoke()方法如下
===============到此为止,Hessian的一个rpc调用就全部结束了public void invoke(Object service, AbstractHessianInput in, AbstractHessianOutput out) throws Exception { ServiceContext context = ServiceContext.getContext(); // backward compatibility for some frameworks that don't read // the call type first in.skipOptionalCall(); // Hessian 1.0 backward compatibility String header; while ((header = in.readHeader()) != null) { Object value = in.readObject(); context.addHeader(header, value); } //从流中读取方法名信息 String methodName = in.readMethod(); //从流中读取参数长度信息 int argLength = in.readMethodArgLength(); Method method; method = getMethod(methodName + "__" + argLength); if (method == null) //根据方法名获得方法,在创建类的实例的时候已经将方法名和方法实例放到了缓存中,这里直接获取就可以了 method = getMethod(methodName); if (method != null) { } else if ("_hessian_getAttribute".equals(methodName)) { String attrName = in.readString(); in.completeCall(); String value = null; if ("java.api.class".equals(attrName)) value = getAPIClassName(); else if ("java.home.class".equals(attrName)) value = getHomeClassName(); else if ("java.object.class".equals(attrName)) value = getObjectClassName(); out.writeReply(value); out.close(); return; } else if (method == null) { out.writeFault("NoSuchMethodException", "The service has no method named: " + in.getMethod(), null); out.close(); return; } Class<?> []args = method.getParameterTypes(); if (argLength != args.length && argLength >= 0) { out.writeFault("NoSuchMethod", "method " + method + " argument length mismatch, received length=" + argLength, null); out.close(); return; } Object []values = new Object[args.length]; //从流中读取具体的请求参数信息 for (int i = 0; i < args.length; i++) { // XXX: needs Marshal object values[i] = in.readObject(args[i]); } Object result = null; try { //通过反射执行具体的方法,并获取返回值result result = method.invoke(service, values); } catch (Exception e) { Throwable e1 = e; if (e1 instanceof InvocationTargetException) e1 = ((InvocationTargetException) e).getTargetException(); log.log(Level.FINE, this + " " + e1.toString(), e1); out.writeFault("ServiceException", e1.getMessage(), e1); out.close(); return; } // The complete call needs to be after the invoke to handle a // trailing InputStream in.completeCall(); 将方法返回值写入到输出流中 out.writeReply(result); //关闭连接 out.close(); }