Servlet生命周期详解

Java Servlet生命周期及方法详解

一、问题介绍

在java项目的实际开发中,很多项目都是java web项目,这里就离不开servlet,了解servlet的生命周期及处理流程对于掌握web开发十分重要。

二、实例及源码讲解

2.1、servlet的生命周期

我们一般都了解servlet的大致生命周期为:init->service->get\post\...->destroy,如下图:

该图只是说明了各个方法的前后调用顺序,没有具体说明各个方法的调用时机。我们可以通过解读源码来了解具体各个方法的调用时机。

我们编写了一个样例应用来了解具体的调用时机。项目依赖如下:


apply plugin: "java"
apply plugin: "idea"
apply plugin: "jetty"

idea {
    module {
        downloadSources = true
        downloadJavadoc = true
    }

}

group 'com.iwill'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
   // providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.0'
    compile group: 'org.mortbay.jetty', name: 'servlet-api', version: '2.5-20081211'
    compile group: 'org.mortbay.jetty', name: 'jetty', version: '6.1.26'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

jettyRun {
    httpPort 8080
    contextPath project.name
    scanIntervalSeconds 0
    reload "automatic"
}

这里依赖jetty的servlet-api和jetty的原因是我们在使用jetty作为服务器时(本地,生产环境一般是tomcat),通过打印加载的类信息来知道对应的servlet的group及版本的。样例代码:

package com.iwill.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(" invoke get method ");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(" invoke post method ");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(" invoke service method ");
        super.service(req, resp);
    }

    @Override
    public void destroy() {
        System.out.println(" invoke destroy method ");
        super.destroy();
    }

    @Override
    public void init() throws ServletException {
        System.out.println(" invoke init method ");
        super.init();
    }
}

通过在init方法上添加断点,可以看到,在访问/hello时,会被org.mortbay.jetty.servlet.ServletHolder#handle处理

0255ab1846b0ba015c4b93ef3c7b16f329c.jpg

org.mortbay.jetty.servlet.ServletHolder#handle的代码如下:

 /** Service a request with this servlet.
     */
    public void handle(ServletRequest request,
                       ServletResponse response)
        throws ServletException,
               UnavailableException,
               IOException
    {
        if (_class==null)
            throw new UnavailableException("Servlet Not Initialized");
        
        Servlet servlet=_servlet;
        synchronized(this)
        {
            if (_unavailable!=0 || !_initOnStartup)
                servlet=getServlet();
            if (servlet==null)
                throw new UnavailableException("Could not instantiate "+_class);
        }
        
        // Service the request
        boolean servlet_error=true;
        Principal user=null;
        Request base_request=null;
        try
        {
            // Handle aliased path
            if (_forcedPath!=null)
                // TODO complain about poor naming to the Jasper folks
                request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);

            // Handle run as
            if (_runAs!=null && _realm!=null)
            {
                base_request=HttpConnection.getCurrentConnection().getRequest();
                user=_realm.pushRole(base_request.getUserPrincipal(),_runAs);
                base_request.setUserPrincipal(user);
            }
            
            servlet.service(request,response);
            servlet_error=false;
        }
        catch(UnavailableException e)
        {
            makeUnavailable(e);
            throw _unavailableEx;
        }
        finally
        {
            // pop run-as role
            if (_runAs!=null && _realm!=null && user!=null && base_request!=null)
            {
                user=_realm.popRole(user);
                base_request.setUserPrincipal(user);
            }

            // Handle error params.
            if (servlet_error)
                request.setAttribute("javax.servlet.error.servlet_name",getName());
        }
    }

从上面的代码可以看出,首先会调用 servlet=getServlet(),后面再调用servlet.service(request,response)。

getServlet()方法代码如下:

 public synchronized Servlet getServlet()
        throws ServletException
    {
        // Handle previous unavailability
        if (_unavailable!=0)
        {
            if (_unavailable<0 || _unavailable>0 && System.currentTimeMillis()<_unavailable)
                throw _unavailableEx;
            _unavailable=0;
            _unavailableEx=null;
        }

        if (_servlet==null)
            initServlet();
        return _servlet;
    }

如果_servlet==null,会去调用initServlet(),该方法的源码如下:

private void initServlet() 
    	throws ServletException
    {
        Principal user=null;
        try
        {
            if (_servlet==null)
                _servlet=(Servlet)newInstance();
            if (_config==null)
                _config=new Config();
            
            //handle any cusomizations of the servlet, such as @postConstruct
            if (!(_servlet instanceof SingleThreadedWrapper))
                _servlet = getServletHandler().customizeServlet(_servlet);
            
            // Handle run as
            if (_runAs!=null && _realm!=null)
                user=_realm.pushRole(null,_runAs);
            
            _servlet.init(_config);
        }
        catch (UnavailableException e)
        {
            makeUnavailable(e);
            _servlet=null;
            _config=null;
            throw e;
        }
        catch (ServletException e)
        {
            makeUnavailable(e.getCause()==null?e:e.getCause());
            _servlet=null;
            _config=null;
            throw e;
        }
        catch (Exception e)
        {
            makeUnavailable(e);
            _servlet=null;
            _config=null;
            throw new ServletException(e);
        }
        finally
        {
            // pop run-as role
            if (_runAs!=null && _realm!=null && user!=null)
                _realm.popRole(user);
        }
    }

该方法会构造servlet和config,并且调用servlet.init(),即我们javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)方法,这个方法中会去调用javax.servlet.GenericServlet#init(),这是一个空方法,如果我们需要为我们的servlet进行特别的初始化设置,我们可以覆盖父类(HttpServlet的init方法)。

这里我们弄清楚了我们servlet的init方法的调用时机,即第一次调用我们的servlet时会去调用init方法。我们跟踪org.mortbay.jetty.servlet.ServletHolder#handle方法中的servlet.service(request,response),会来到javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)servlet.service(request,response)。该方法的代码如下:

    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);
    }
}

即将一般的ServletRequest、ServletResponse转化为HttpServletRequest、HttpServletResponse,然后调用自身的service方法,该方法实现如下:

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 = req.getDateHeader(HEADER_IFMODSINCE);
		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
		    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
		    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);
	}
    }

根据method调用对应的方法。

根据上述分析可知,Servlet的详细生命周期如下:

在第一次调用servlet时,会进行初始化(即调用init方法);每次调用时都会调用service方法,该方法的作用就是请求分发到具体的方法(get、post等);最后在容器关闭时,会调用destroy方法。因此,除了init、destroy方法是调用一次外,其他方法可以多次调用。

2.2、servlet的类继承关系及各个方法说明

2.2.1、servlet的类继承关系如下:

servlet和servletConfig是接口,servletConfig定义了在初始化时,容器传给servlet的信息,servlet定义了所有servlet都需要实现的方法。

GenericServlet是一个抽象类,定义了通用的、协议无关的servlet。

HttpServlet是一个抽象类,定义了基于web应用的servlet,如果要自己编写web应用的servlet,继承HttpServlet,并且必须要重写对应的方法(doGet\doPost\...)。

2.2.2、servlet的各方法说明

init方法:servlet第一次被调用的时候调用,被调用之后,该servlet可以接受外部的请求,并且只调用一次。我们实现servlet时,可以在init方法中进行一次初始化工作,比如说,数据库连接池初始化等。

service:servlet接收请求的入口,并且在该方法被调用前,一定要保证init被调用过。并且,在多线程并发环境下,需要使用同步机制来保证共享资源的访问。

destroy:容器调用destroy方法后,该servlet就不会再对外提供服务了。但是,如果自己在servlet调用destroy方法,不会有什么影响。一般会在该方法中进行资源的关闭操作,例如:数据库线程池的关闭等。

 

三、实例代码

上述涉及到的实例代码见: https://github.com/yangjianzhou/servlet-lifecycle

 

转载于:https://my.oschina.net/yangjianzhou/blog/1934286

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值