Hessian源码学习(一)服务端

【服务端】
HessianServlet 是一个非常普通的Servlet 它直接继承 GenericServlet

我们看其中两个核心方法:init与service方法(参考源码版本3.0.13)
[size=medium]
[b]init(ServletConfig config)[/b][/size]

该方法覆写父类的init(ServletConfig config)方法(按照servlet规范推荐,覆写init()方法更好)

在初始化中,它做了哪些事情呢?

[b]第一步.初始化远程服务类(Impl)[/b]

if (_homeImpl != null) {
}
else if (getInitParameter("home-class") != null) {
String className = getInitParameter("home-class");

Class homeClass = loadClass(className);

_homeImpl = homeClass.newInstance();

init(_homeImpl);
}
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;
}

其中home-class或者service-class就是在web.xml配置的提供远程服务的类(两者取一个作为配置即可);

它首先通过getInitParameter方法获取类名,然后调用loadClass获取对应的Class,最后newInstance创建实例;

若用户未配置,[b]使用当前Servlet作为默认实现类[/b](不能为HessianServlet);

我们接着往下看:init(_homeImpl) 又做了些什么呢?

private void init(Object service)
throws ServletException
{
if (service instanceof Service)
((Service) service).init(getServletConfig());
else if (service instanceof Servlet)
((Servlet) service).init(getServletConfig());
}

根据当前服务类(impl)继承的接口(可以是标准的servlet接口也可以是Hessian中定义的Service接口)做相应的初始化(相当于spring的init标签属性);

[b]第二步.初始化远程服务类接口(基本上Hessian也是通过这接口知道自己对外暴露了哪些服务)[/b]

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) {
_homeAPI = findRemoteAPI(_homeImpl.getClass());

if (_homeAPI == null)
_homeAPI = _homeImpl.getClass();
}
}

其中home-api或是api-class定义了对外暴露的服务接口(服务的使用方需要依赖这个接口jar包);

若在配置文件中未指定接口,首先尝试从当前服务类查找(findRemoteAPI);

findRemoteAPI是一个递归方法:在类继承路径上递归查找只继承一个接口的类
private Class findRemoteAPI(Class implClass)
{
if (implClass == null || implClass.equals(GenericService.class))
return null;

Class []interfaces = implClass.getInterfaces();

if (interfaces.length == 1)
return interfaces[0];

return findRemoteAPI(implClass.getSuperclass());
}

若查找失败,则使用当前服务类作为interface Class(这句话说得很奇怪,还是看代码比较明了)

[b]第三步.初始化 HessianSkeleton 类(相当于HessianExecutor)[/b]

public HessianSkeleton(Object service, Class apiClass)
{
super(apiClass);

_service = service;

if (! apiClass.isAssignableFrom(service.getClass()))
throw new IllegalArgumentException("Service " + service + " must be an instance of " + apiClass.getName());
}

重点在于super(apiClass):AbstractSkeleton的初始化

protected AbstractSkeleton(Class apiClass)
{
_apiClass = apiClass;

Method []methodList = apiClass.getMethods();

for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];

if (_methodMap.get(method.getName()) == null)
_methodMap.put(method.getName(), methodList[i]);

Class []param = method.getParameterTypes();
String mangledName = method.getName() + "__" + param.length;
_methodMap.put(mangledName, methodList[i]);

_methodMap.put(mangleName(method, false), methodList[i]);
}
}

Method []methodList = apiClass.getMethods();

获取服务接口中(_homeAPI)所有public方法,这也是为什么hessian说:[b]每一个public方法都是一个远程服务。[/b]

接着遍历所有方法,放入_methodMap中,这里有个关键点,如何支持重载?

Hessian是这样处理的,比如对于一个方法: public void sayHello(String str),那么在_methodMap中会存放三个key(这三个key对应着同一个value,也就是sayHello方法)
[b]sayHello
sayHello_1 (参数个数)
sayHello_string (参数类型)[/b]

所以只要client传递方法说明支持调用重载方法,那么他就会调用到正确的重载方法,不然结果是不确定的。
[size=medium]
[b]service(ServletRequest request, ServletResponse response)[/b][/size]

这个方法重写了父类(GenericServlet)的service方法

第一步.检查请求方式是否是post

if (! req.getMethod().equals("POST")) {
res.setStatus(500, "Hessian Requires POST");
PrintWriter out = res.getWriter();

res.setContentType("text/html");
out.println("<h1>Hessian Requires POST</h1>");

return;
}

第二步.初始化Hessian输入流/输出流

InputStream is = request.getInputStream();
OutputStream os = response.getOutputStream();

HessianInput in = new HessianInput(is);
HessianOutput out = new HessianOutput();
out.setSerializerFactory(getSerializerFactory()); // 关于这里的序列化工厂,后面的文章会详述
out.init(os);

第三步.调用服务(这就是RPC中的C哈 ^_^)

_homeSkeleton.invoke(in, out);

我们具体看下HessianSkeleton.invoke方法:

//反序列化输入流获取调用的服务方法
String methodName = in.readMethod();
Method method = getMethod(methodName);

//反序列输入流获取方法参数
Class []args = method.getParameterTypes();
Object []values = new Object[args.length];

for (int i = 0; i < args.length; i++)
values[i] = in.readObject(args[i]);

// 真正的执行服务方法
result = method.invoke(_service, values);


//最后把执行写回输出流,输出起始标志:'r' 1 0
out.startReply();
// 序列化结果(远程调用结果)
out.writeObject(result);
// 输出结束标志'z'
out.completeReply();

[size=medium][b]HessianServlet总结[/b][/size]
通过上面的源码分析,我们了解到:

1.Hessian如何响应一个远程调用;

2.在web.xml中的各项配置都是什么意思,以及Hessian相应的默认处理规则;

3.Hessian如何支持方法重载;

在下一篇文章中,我们将会分析客户端如何调用Hessian服务!欢迎朋友们提出宝贵意见!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值