您的位置:寻梦网首页编程乐园Java天地JSP 专辑JSP001 HTML 离线版
Java 天地
JSP001 HTML 离线版
精选文章 >> Servlet >> Servlet深层讨论

由 amtd 发布于: 2001-02-27 10:56

Post

          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]
__________________



资料来源: JSP001.com