Hprose for Java 服务器(三)

宋正真
2023-12-01
[b][size=x-large]自己编写Servlet发布Hprose服务[/size][/b]

如果您对直接使用HproseServlet发布服务还有什么不满的话,当然可以自己编写一个Servlet来发布Hprose服务。而且您会发现这并不是什么难事,因为Hprose已经为您提供了很好的基础,您只需要对HproseServlet做一下扩展,或者直接使用HproseHttpService来构建自己的Servlet即可。

[b][size=large]扩展HproseServlet[/size][/b]

我们先来介绍最简单的方式,那就是以HproseServlet为基类,通过创建子类的方式来扩展HproseServlet。使用这种方式,您可以更灵活的控制方法的发布。

在前面直接使用HproseServlet发布服务时,您会发现我们只能以类为单位来发布方法,而不能单独发布类或对象上的某一个或几个方法,另外,如果我们想对某一个方法指定别名,而不是对整个对象或类上所有方法增加名称空间(别名前缀)的话,也是做不到的。

上面这些需求您都可以通过扩展HproseServlet来实现。

创建HproseServlet的子类非常容易,HproseServlet提供了多个可以覆盖的方法,其中最重要是:setGlobalMethods。

该方法的参数类型是HproseMethods类型的,下面我们先来对HproseMethods类型做一下介绍。

[b]HproseMethods类型[/b]

HproseMethods类型用于表示发布方法的集合。

它有以下几个方法:

1. addMethod
2. addMethods
3. addInstanceMethods
4. addStaticMethods
5. addMissingMethod

下面我们对这几个方法分别做一下介绍。

[i][b]addMethod方法[/b][/i]

addMethod用来控制单个方法的发布,它有十种重载形式。通过它可以发布任意对象上的任意public实例方法,或任意类上的任意public静态方法,并且可以为每个方法都指定别名。当您发布的方法具有相同个数参数的重载时,使用addMethod是唯一可行的方法。

[i][b]addMethods方法[/b][/i]

addMethods用来控制一组方法的发布,它有九种重载形式。如果您所发布的方法来自同一个对象,或是同一个类,使用addMethods方法通常比直接使用addMethod方法更为方便。因为您不但可以为每个方法都指定别名,还可以为这一组方法指定同一个名称空间(别名前缀)。但实际上我们很少情况会直接使用它。因为有更加简单的addInstanceMethods和addStaticMethods方法。

[i][b]addInstanceMethods方法[/b][/i]

addInstanceMethods用来发布指定对象上的指定类层次上声明的所有public实例方法。它有四种重载形式。如果您在使用addInstanceMethods方法时,不指定类层次,则发布这个对象所在类上声明的所有public实例方法。这个方法也支持指定名称空间(别名前缀)。

[i][b]addStaticMethods方法[/b][/i]

addStaticMethods用来发布指定类上声明的所有public静态方法。它有两种重载形式。这个方法也支持指定名称空间(别名前缀)。

[i][b]addMissingMethod方法[/b][/i]

这是一个很有意思的方法,它用来发布一个特定的方法,当客户端调用的方法在服务器发布的方法中没有查找到时,将调用这个特定的方法。它有两种重载形式。

使用addMissingMethod发布的方法可以是实例方法,也可以是静态方法,但是只能发布一个。如果多次调用addMissingMethod方法,将只有最后一次发布的有效。

用addMissingMethod发布的方法参数应为以下形式:
(String name, Object[] args)

第一个参数表示客户端调用时指定的方法名,方法名在传入该方法时全部是小写的。

第二个参数表示客户端调用时传入的参数列表。例如客户端如果传入两个参数,则args的数组长度为2,客户端的第一个参数为args的第一个元素,第二个参数为args的第二个元素。如果客户端调用的方法没有参数,则args为长度为0的数组。

现在,您对HproseMethods的以上方法应该有一定程度的认识了,下面我们再结合HproseServlet的setGlobalMethods方法来看一下如何实际应用它们。

[b]setGlobalMethods方法[/b]

setGlobalMethods方法的定义形式如下:
protected void setGlobalMethods(HproseMethods methods) {
}

实际上它是在HproseServlet的init方法最后被调用的,其参数为全局发布方法的集合。当我们需要在全局发布方法时,就可以覆盖这个方法来实现。

例如我们只想要发布一个Exam2对象上的getID方法,通过直接配置HproseServlet并不能得到我们想要的效果,那么通过覆盖setGlobalMethods,我们就可以实现这个功能了。

下面看我们自己定义的Servlet:
package hprose.exam;
import hprose.server.HproseServlet;
import hprose.common.HproseMethods;
public class MyHproseServlet extends HproseServlet {
protected void setGlobalMethods(HproseMethods methods) {
Exam2 exam2 = new Exam2();
methods.addMethod("getID", exam2);
}
}

是不是很简单,只需要9行代码,就完成我们自己的HproseServlet了。

接下来我们看看如何使用HproseHttpService来构建Servlet。

[b][size=large]使用HproseHttpService来构建Servlet[/size][/b]

通常我们是不需要使用HproseHttpService来构建Servlet的,因为通过配置和扩展HproseServlet已经几乎可以满足所有要求了。

但您仍然有理由来使用HproseHttpService构建Servlet,例如:您可能希望发布服务能够与Spring集成;或者您可能不喜欢配置文件,而希望通过使用注解(annotation)方式来发布服务。当然,您还可能希望不在全局范围内发布方法,而是为每个请求或每个会话来创建对象并发布其上的方法。

在这种情况下,使用HproseHttpService来构建Servlet将是个好主意。

但是我们并不打算在这里介绍如何编写与Spring集成发布服务的Servlet,也没有打算介绍如何编写一个使用注解(annotation)方式发布服务的Servlet。因为这将花费不少篇幅,并且大部分代码脱离了我们要重点介绍的内容。

我们这里主要来讲解在使用HproseHttpService来构建自己的Servlet时,需要注意的一些问题,以及如何为每个请求或每个会话来创建对象并发布其上的方法。

[b]按请求发布方法[/b]

这里我们还是以发布Exam2上面的方法为例,来说明如何编写Servlet:
package hprose.exam;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import hprose.server.HttpContext;
import hprose.server.HproseHttpMethods;
import hprose.server.HproseHttpService;
public class MyHproseServlet2 extends HttpServlet {
private HproseHttpService service = new HproseHttpService();
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
Exam2 exam2 = new Exam2();
HproseHttpMethods methods = new HproseHttpMethods();
methods.addInstanceMethods(exam2);
methods.addInstanceMethods(exam2, Exam1.class);
service.handle(new HttpContext(request, response, this.getServletConfig(),this.getServletContext()),
methods);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public String getServletInfo() {
return "Hprose Servlet 1.0";
}
}


您完全可以把上面的例子作为模版来改写成您自己的Servlet。

在这个例子中,关键的方法就是processRequest方法。

首先创建HproseHttpMethods类的对象,之后通过该对象上的addInstanceMethods方法发布Exam2对象上的方法,最后通过HproseHttpService对象的handle方法来处理请求。handle方法的第一个参数是HttpContext类型,这里需要通过创建HttpContext对象实例来初始化当前上下文,第二个参数就是我们要发布的方法集合。

按请求发布方法时,发布的方法不必是线程安全的,按会话或者全局发布的方法应该保证方法本身是线程安全的。按请求发布方法的情况不多,但是按会话发布方法的情况却可能经常会遇到,下面我们就来说明一下如何按会话来发布方法。

[b]按会话发布方法[/b]

因为按会话发布和按请求发布的主要区别在processRequest方法上,所以我们下面的例子中,只写这一个方法。

   protected void processRequest(HttpServletRequest request, HttpServletResponse 
response)
throws ServletException, IOException {
HttpSession session = request.getSession();
HproseHttpMethods methods = (HproseHttpMethods) session.getAttribute("exam2");
if (methods == null) {
Exam2 exam2 = new Exam2();
methods = new HproseHttpMethods();
methods.addInstanceMethods(exam2);
methods.addInstanceMethods(exam2, Exam1.class);
session.setAttribute("exam2", methods);
}
service.handle(new HttpContext(request, response, this.getServletConfig(), this.getServletContext()),
methods);
}


在这个例子中我们首先获取了session对象。之后,我们查询session中是否已经包含了发布的方法集合,如果有就直接使用它,否则通过new运算符创建一个新的,在添加完发布的方法后,将其以exam2为名放入session中。之后就是发布exam2上的方法了。

这个例子很好懂,这里就不再多作解释啦。

HproseHttpService还有许多方法和属性,可以直接参见HproseServlet源码来了解它们的用法。

[b][size=x-large]自己编写JSP发布Hprose服务[/size][/b]

使用JSP来发布Hprose很简单,也是使用HproseHttpService来实现,但这是最不常用的方式,当然也是不推荐的方式。因为使用JSP方式相对于Servlet来说效率较低。

所以,下面我们仅用一个简单的例子来结束本章,不再对例子作详细说明,因为有了前面的基础,相信您一定很容易看懂下面的例子:
<%@page contentType="text/plain" pageEncoding="UTF-8"%>
<%@page import="hprose.server.*"%>
<%@page import="hprose.exam.*"%>
<jsp:useBean id="service" scope="application" class="hprose.server.HproseHttpService" />
<jsp:setProperty name="service" property="debugEnabled" value="true" />
<%
Exam2 exam2 = new Exam2();
HproseHttpMethods methods = new HproseHttpMethods();
methods.addInstanceMethods(exam2);
methods.addInstanceMethods(exam2, Exam1.class);
service.handle(new HttpContext(request, response, config, application), methods);
%>

在某些服务器上用JSP方式发布服务,可能会出现一下错误信息:
[code]java.lang.IllegalStateException: getOutputStream() has already been called for this response
[/code]
解决方法只需要在页面最后,加入以下语句即可:
out.clear();
out = pageContext.pushBody();

另外要注意保存JSP文件时请使用UTF-8编码,并去掉BOM。
 类似资料: