本篇博客我们来看一下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()方法如下
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();
}===============到此为止,Hessian的一个rpc调用就全部结束了

本文介绍了Hessian Server端的配置方式及其在web.xml中的实现细节,深入剖析了HessianServlet的初始化过程和HTTP请求处理流程,包括如何通过反射机制调用远程方法。
2871

被折叠的 条评论
为什么被折叠?



