精选文章
>> Servlet
>> Servlet深层讨论
由 amtd 发布于: 2001-02-27 10:56
Servlet深层讨论 (作者:doctor)
Servlets(Java 小服务器程序)是Java中新增加的一个全新功能。一般来说,Servlets是由服务器端调用和执行的任何Java类,浏览器端运行的Java程序叫Applet,Web服务器端运行的Java程序叫做Servlet。自从有了Servlet后,Java的电子商务才真正的开始,之后的JSP又是在Servlet上有了更进一步的发展。了解Servlet的机制也就掌握到了到JSP的实现原理。
我们在编写Servlet的时候不需要关心一个Servlet是如何被装载、服务器环境,只需要调用Java Servlet API编程接口就行了。在使用Servlet API的时候,程序员完全可以不必了解内部运行方式,服务器头、Cookies、会话都可以通过Servlet来处理。但当我们需要一些特殊功能的时候,就需要了解它的一些实现机制了。
先从Servlet的生命周期说起。一般情况下,可以归纳为几点:
1. 装载Servlets。这项操作一般是动态执行的。有些Server提供了相应的管理功能,可以在启动的时候就装载Servlet;
2. Server创建一个Servlet实例;
3. Server调用Servlet的init()方法;
4. 一个客户端的请求到达Server;
5. server创建一个请求对象;
6. Server创建一个响应对象;
7. Server激活Servlet的Service()方法,并传递请求和响应对象;
8. service()方法获得关于请求对象的的信息、处理请求、访问其他资源、获得需要的信息;
9. service()方法使用响应对象的方法,将响应传回Server,最终到达客户端。Service()方法可能机或其他方法已处理请求,如doGet()或doPost()或程序员自己开发的方法;
10. 对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用init()方法。也就是说,Servlet()只初始化一次。
11. 当Server不再需要Servlet时(一般是当Server关闭的时候),Server调用Servlets的destroy()方法。
从前面我们还可以更进一步得出以下几点:
Servlet运行时是多线程的,在开发的时候由Servlet API实现,开发人员不需要做特别的处理。
Servlet的init()、destroy()的使用就像是一个C++编写的结构、析构函数,要注意前后的一致。比如在init()打开了一个数据库,就要在destroy()中关闭。
对一些底层的处理,如要控制消息响应的方式,我们就要重载server()方法,并重新编写。
为便于解释Servlet的实现机制,围绕HttpServlet,用下图描述Javax.Http包中常用到的类之间的关系(除掉了JSP部分),限于篇幅,没有给出每个接口、类的属性和方法。通过图示,我们很容易开发一些较低层次的代码。
从上图,非常明显的可以看出HttpServlet是从GenericServlet类继承下来的。而事实上,GenericServlet类主要是为了起到三个接口的联合(这个词用得可能不科学):Servlet,ServletConfig,Serializable,以获得系统的支持;除了init()外几乎没有实现方法操作。真正调用系统所支持的各项功能是在HttpServlet类中。
就HttpServlet的service()方法,一般来说,当它接收到一个OPTIONS请求时,会调用doOptions()方法,当接收到一个TRACE请求时调用doTrace()。doOptions()缺省执行方式是自动决定什么样的HTTP被选择并且返回哪个信息。相关源代码如下:
public void service(ServletRequest req,ServletResponse res)
throws ServletException,IOException {
HttpServletRequest request;
HttpServletResponse response;
try
{
request = (HttpServletRequest)req;
response = (HttpServletResponse)res;
}
catch(ClassCastException _ex)
{
throw new ServletException("non-HTTP request or response");
} service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if(method.equals("GET"))
{
long lastModified = getLastModified(req);
if(lastModified == -1L)
{
doGet(req, resp);
}
else
{
long ifModifiedSince = req.getDateHeader("If-Modified-Since");
if(ifModifiedSince < (lastModified / 1000L) * 1000L)
{
maybeSetLastModified(resp, lastModified); doGet(req, resp);
}
else
{
resp.setStatus(304);
}
}
}
else if(method.equals("HEAD"))
{
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
}
else
if(method.equals("POST"))
doPost(req, resp);
else
if(method.equals("PUT"))
doPut(req, resp);
else
if(method.equals("DELETE"))
doDelete(req, resp);
else
if(method.equals("OPTIONS"))
doOptions(req, resp);
else
if(method.equals("TRACE")) {
doTrace(req, resp);
}
else
{
String errMsg = lStrings.getString("http.method_not_implemented");
Object errArgs[] = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
Method methods[] = getAllDeclaredMethods(getClass());
boolean ALLOW_GET = false; boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false; boolean ALLOW_PUT = false;
boolean ALLOW_DELETE = false; boolean ALLOW_TRACE = true;
boolean ALLOW_OPTIONS = true;
for(int i = 0; i < methods.length; i++)
{
Method m = methods[i];
if(m.getName().equals("doGet"))
{
ALLOW_GET = true;
ALLOW_HEAD = true;
}
if(m.getName().equals("doPost"))
ALLOW_POST = true;
if(m.getName().equals("doPut"))
ALLOW_PUT = true;
if(m.getName().equals("doDelete"))
ALLOW_DELETE = true;
}
String allow = null;
if(ALLOW_GET && allow == null)
allow = "GET";
if(ALLOW_HEAD)
if(allow == null)
allow = "HEAD";
else allow = allow + ", HEAD";
if(ALLOW_POST)
if(allow == null)
allow = "POST";
else allow = allow + ", POST";
if(ALLOW_PUT)
if(allow == null)
allow = "PUT";
else allow = allow + ", PUT";
if(ALLOW_DELETE)
if(allow == null)
allow = "DELETE";
else allow = allow + ", DELETE";
if(ALLOW_TRACE)
if(allow == null)
allow = "TRACE";
else allow = allow + ", TRACE";
if(ALLOW_OPTIONS)
if(allow == null)
allow = "OPTIONS";
else allow = allow + ", OPTIONS";
resp.setHeader("Allow", allow);
}
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String CRLF = "\r\n";
String responseString = "TRACE " + req.getRequestURI() + " " + req.getProtocol();
for(Enumeration reqHeaderEnum = req.getHeaderNames();
reqHeaderEnum.hasMoreElements();)
{
String headerName = (String)reqHeaderEnum.nextElement();
responseString = responseString + CRLF + headerName + ": " + req.getHeader(headerName);
}
responseString = responseString + CRLF; int responseLength = responseString.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(responseString);
out.close();
}
如果我们改写了上面的代码,将可以写出一些针对性更强的特殊领域中的应用。限于篇幅,这里将不在举例。为了便于读者的分析,将随邮件发送javax.servlet.http包中的源代码。 在HttpServlet类(或子类)中,可以对一些系统底层支持的功能进行一些操作,比如Session、Cookie等。其中HttpSession接口在Sun公司提供的包中没有找到它的类,应该是由Server引擎来实现、管理它的相关功能。笔者曾试着扩展HttpSession以实现一些额外的功能,但由于Session与另外几个接口Request、Response等结合非常紧密,又是系统直接支持,所以没有成功。
笔者注:由于 Java虚拟机的特点,使我们能够了解到一些比较底层的包。但又因为Java引擎的强大功能,组件式的调用为更深入的分析带来了很大的困难。如果有错误还恳请读者指正,也欢迎对此感兴趣的朋友能够经常交流,共同提高我们的Java应用水平。我的邮件地址是jiangcheng@staff.yesky.com
[已被 amtd 编辑过, 在 2001-03-05 10:42]
__________________
|