Servlet生命周期

Posted 秋风Haut

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet生命周期相关的知识,希望对你有一定的参考价值。

第一篇博客,复习下servlet的生命周期。
servlet生命周期可以分为三个阶段:
1. 初始化阶段

    servlet在初始化阶段会调用init()方法,是在servlet容器加载servlet之后进行的,在以下的三个阶段,servlet容器会加载serlvet
        1)在servlet容器启动时,它会自动加载某些serlvet,但是需要你在配置web.xml中配置serlvet的配置中添加<load-on-startup>1</load-on-startup>
        2 )servlet容器启动状态下,用户首次向servlet发送请求时
             3)  servlet类文件被更新后,servlet会被重新加载
        (注意:在web.xml文件中,以上第一种Servlet只有<serlvet>元素,没有<servlet-mapping>元素,这样我们无法通过url的方式访问这些Servlet,这种Servlet通常会在<servlet>元素中配置一个<load-on-startup>子元素,让容器在启动的时候自动加载这些Servlet并调用init()方法,完成一些全局性的初始化工作。)
    servlet容器装载完了serlvet,就会生成servlet实例,并调用init()方法对servelt进行初始化,servlet进入第二阶段。注:init()方法在servlet一生中,只会调用一次
  1. 响应用户请求阶段

servlet处理用户的过程是这样的:当客户发起一个请求,serlvet容器就会调用service()方法,同时,request对象和response对象会作为参数传进service,而service方法会根据request对象中的method方法来得知用户使用的哪种提交方法xxx,来调用相应的doXxx()方法来处理用户的请求。定义的提交方法有:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE这七种,但是最常用的就是get和post两种。

612:12
        protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException 

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) 
          long lastModified = getLastModified(req);
     if (lastModified == -1) 
         // servlet doesn't support if-modified-since, no reason
        // to go through further expensive logic
        doGet(req, resp);
     else 
    long ifModifiedSince;
    try 
        ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
     catch (IllegalArgumentException iae) 
    // Invalid date header - proceed as if none was set
    ifModifiedSince = -1;
    
    if (ifModifiedSince < (lastModified / 1000 * 1000)) 
    // If the servlet mod time is later, call doGet()
    // Round down to the nearest second for a proper compare
                                                                        maybeSetLastModified(resp,lastModified);
    doGet(req, resp);
     else              resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            
        
     else if (method.equals(METHOD_HEAD)) 
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
     else if (method.equals(METHOD_POST)) 
        doPost(req, resp);
     else if (method.equals(METHOD_PUT)) 
        doPut(req, resp);
     else if (method.equals(METHOD_DELETE)) 
        doDelete(req, resp);
     else if (method.equals(METHOD_OPTIONS)) 
         doOptions(req,resp);
     else if (method.equals(METHOD_TRACE)) 
         doTrace(req,resp);
     else 
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//      
String errMsg =lStrings.getString("http.method_not_implemented");
     Object[] errArgs = new Object[1];
    errArgs[0] = method;
    errMsg = MessageFormat.format(errMsg, errArgs);
    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    

……
717:5
public void service(ServletRequest req, ServletResponse res)
                throws ServletException, IOException 

    HttpServletRequest  request;
    HttpServletResponse response;

    try 
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
     catch (ClassCastException e) 
        throw new ServletException("non-HTTP request or response");
    
    service(request, response);
在其中我们可以看到service()方法在HttpServlet中的处理其实有两个,一个是protected,另一个public,public修饰的serice()方法的作用是把所有通用的request类和response类,强转为http协议专用的request和response,然后才执行真正的service()方法、

通过查看源码,我们会发现,所有的serlet都会继承HttpServlet类,而HttpServlet类会继承GenericServlet类,看名字,我们就可以知道,GenericServlet类是通用于所有协议的,而HttpServlet类仅适用于http协议。
可是在这里我们依旧找不到servlet的生命周期的影子,别急,再往上找,我们会发现GenericServlet类实现了Servlet接口和ServletConfig接口,其中的Servlet接口的最重要的三个方法:init()、service()、destroy()就是servlet的生命中最重要的三个阶段。另外两个分别是getServletConfig()、getServletInfo()不重要,请分别意会。
这是三个方法的来历。
而doGet()方法和doPost()方法,我们看HttpServlet类自己的doGet()和doPost()方法,都是给出错误,因此我们必须要自己重写这些方法。

其它五个:
doHead:
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) 
                        doGet(req, resp);
                     else 
                        NoBodyResponse response = new NoBodyResponse(resp);
                        doGet(req, response);
                        response.setContentLength();
                    
我们看出,它最终会转向doGet()方法
    doPut():
 String protocol = req.getProtocol();
                String msg = lStrings.getString("http.method_put_not_supported");
                if (protocol.endsWith("1.1")) 
                    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
                 else 
                    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                
会给出错误信息。
doDelete():
 String protocol = req.getProtocol();
                String msg = lStrings.getString("http.method_delete_not_supported");
                if (protocol.endsWith("1.1")) 
                    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
                 else 
                    resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
                
会给出错误信息。
doOptions():
Method[] methods = getAllDeclaredMethods(this.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=METHOD_GET;
                if (ALLOW_HEAD)
                    if (allow==null) allow=METHOD_HEAD;
                    else allow += ", " + METHOD_HEAD;
                if (ALLOW_POST)
                    if (allow==null) allow=METHOD_POST;
                    else allow += ", " + METHOD_POST;
                if (ALLOW_PUT)
                    if (allow==null) allow=METHOD_PUT;
                    else allow += ", " + METHOD_PUT;
                if (ALLOW_DELETE)
                    if (allow==null) allow=METHOD_DELETE;
                    else allow += ", " + METHOD_DELETE;
                if (ALLOW_TRACE)
                    if (allow==null) allow=METHOD_TRACE;
                    else allow += ", " + METHOD_TRACE;
                if (ALLOW_OPTIONS)
                    if (allow==null) allow=METHOD_OPTIONS;
                    else allow += ", " + METHOD_OPTIONS;

                resp.setHeader("Allow", allow);
            
    这个方法不需要重写,注释中的意思是:会被servet调用来让servlet来处理一个options请求。这个options请求决定了servet所支持的http方法并返回一个适当的参数。
    doTrace()方法:
int responseLength;

                String CRLF = "\\r\\n";
                StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI())
                    .append(" ").append(req.getProtocol());

                Enumeration<String> reqHeaderEnum = req.getHeaderNames();

                while( reqHeaderEnum.hasMoreElements() ) 
                    String headerName = reqHeaderEnum.nextElement();
                    buffer.append(CRLF).append(headerName).append(": ")
                        .append(req.getHeader(headerName));
                

                buffer.append(CRLF);

                responseLength = buffer.length();

                resp.setContentType("message/http");
                resp.setContentLength(responseLength);
                ServletOutputStream out = resp.getOutputStream();
                out.print(buffer.toString());
                out.close();
                return;
    这个方法同样会被servet调用来让servlet来处理一个TRACE请求。TRACE会返回一个头,这个头将会还用trace请求的方式返回给用户,这样就可以用于debugging。因此也不需要重写。

    总结一个servlet响应请求的过程:
        对于用户到达Servlet的请求,Servlet容器会创建特定于这个请求的ServletRequest对象和ServletResponse对象,然后调用Servlet的service方法。service方法从ServletRequest对象获得用户请求信息,处理该请求,并通过ServletResponse对象向用户返回响应信息。

        然而对于Tomcat来说,它会将传递过来的参数放在一个Hashtable中,该Hashtable的定义是:

        private Hashtable<String String[]> paramHashStringArray = new Hashtable<String String[]>();
        这是一个String-->String[]的键值映射。

        HashMap线程不安全的,Hashtable线程安全。
  1. 销毁阶段:
    当WEB应用被终止,或Servlet容器终止运行,或Servlet容器重新装载Servlet新实例时,Servlet容器会先调用Servlet的destroy()方法,在destroy()方法中可以释放掉Servlet所占用的资源。

以上是关于Servlet生命周期的主要内容,如果未能解决你的问题,请参考以下文章

关于JSP生命周期的叙述,下列哪些为真?

重学SpringBoot系列之生命周期内的拦截过滤与监听

javaWeb中servlet开发——Servlet生命周期

学亮IT手记Servlet的生命周期

servlet的生命周期,啥情况下调用doGet()和doPost()?

Servlet编程:Servlet的生命周期