`

Servlet生命周期

 
阅读更多

 

 

 

 

JavaWeb应用程序的生命周期由Servlet容器控制,Servlet是JavaWeb应用的核心组件,所以知道Servlet生命周期也就知道了JavaWeb应用程序的生命周期。

 

Servlet生命周期分为三个阶段:初始化阶段、运行时阶段、销毁阶段,这三个阶段分别对用Servlet接口中的三个方法

javax.servlet.Servlet.init(ServletConfig)
javax.servlet.Servlet.service(ServletRequest, ServletResponse)
javax.servlet.Servlet.destroy()

init()方法:服务器调用该方法初始化Servlet
service()方法:初始化完毕,服务器调用该方法响应客户的请求
destroy()方法:服务器调用该方法消灭servlet对象
其中,init()方法只在Servlet第一次被请求加载的时候被调用一次,当有客户再请求Servlet服务时,Web服务器将启动一个新的线程,在该线程中,调用service方法响应客户的请求

通过程序来验证Servlet生命周期中所对应的接口,创建一个Servlet别实现这三个方法

public class LifecycleServlet extends HttpServlet {

	public LifecycleServlet() {
		System.out.println("constructor LifecycleServlet");
	}

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

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

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

 访问这个Servlet,并且多次访问,然后关闭Tomcat,我在eclipse下,关闭时是stop the server,不是不是console里面的terminate,控制台输出如下,可以看到多次访问时只调用了service方法

信息: Server startup in 1346 ms
constructor LifecycleServlet
init()
service()
service()
service()
信息: Stopping service Catalina
destroy()

Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

查看HttpServlet源码,ServletRequest被转HttpServletRequest,然后在重载的service中根据请求确实做了分发的操作,源码我删掉了一些与请求分发无关的代码

public abstract class HttpServlet extends GenericServlet {
    @Override
    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);
    }

     protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
           doGet(req, resp);
        } 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 {
            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);
        }
    }
}

Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。HttpServlet指能够处理HTTP请求的servlet(添加与HTTP协议相关API),它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

Servlet接口中的定义的init()签名为

public void init(ServletConfig config) throws ServletException;

LifecycServlet中覆写的init()为

public void init() throws ServletException {}

 自己覆写的和接口定义的不一样,是因为在GenericServlet中被覆写了,看一下源码就知道了,所以继承HttpServlet的类只要覆写init()就可以,因为有参数的init()会调用LifecycServlet中无参数的init()

public abstract class GenericServlet implements Servlet, ServletConfig,java.io.Serializable {

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
        // NOOP by default
    }
}

 

Servlet也可以配置随容器一起启动,在web.xml中配置<load-on-startup>,取值为0-9,数字越小优先级越高

<servlet>
		<servlet-name>LifecycleServlet</servlet-name>
		<servlet-class>lifecycle.LifecycleServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

 console输出,但tomcat启动之LifecycServlet也启动了

十一月 07, 2015 10:30:55 上午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet Engine: Apache Tomcat/7.0.41
十一月 07, 2015 10:30:56 上午 org.apache.catalina.util.SessionIdGenerator createSecureRandom
信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [176] milliseconds.
constructor LifecycleServlet
init()
十一月 07, 2015 10:30:56 上午 org.apache.catalina.startup.HostConfig deployDirectory

 

 Servlet接口中的init()签名是有参数传入的,一个ServletConfig对象,这个对象可以获得web.xml中配置的初始化参数

javax.servlet.Servlet.init(ServletConfig)
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
如何在Servlet中获取这个对象,看一个GenericServlet源码,config对象被私有了,但提供了一个getter()获得该对象
public abstract class GenericServlet implements Servlet, ServletConfig,java.io.Serializable {
    private transient ServletConfig config;
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
}
 编写一个Servlet,获取web.xml中配置的参数
public class ConfigServlet extends HttpServlet {
	@Override
	public void init() throws ServletException {
		ServletConfig servletConfig = super.getServletConfig();
		String param1 = servletConfig.getInitParameter("param1");
		String param2 = servletConfig.getInitParameter("param2");
		System.out.println(param1 + "\t" + param2);
	}
}
 web.xml中的配置,ConfigServlet获取了web.xml中配置的参数,这种配置参数只在当前Servlet范围内有效,其它Servlet无法访问,还有一种配置参数是通过ServletContext获取的,这种是全局的
<servlet>
		<description></description>
		<display-name>ConfigServlet</display-name>
		<servlet-name>ConfigServlet</servlet-name>
		<servlet-class>config.ConfigServlet</servlet-class>
		<init-param>
			<param-name>param1</param-name>
			<param-value>I'm param1</param-value>
		</init-param>
		<init-param>
			<param-name>param2</param-name>
			<param-value>I'm param2</param-value>
		</init-param>
	</servlet>
 看一下ServletConfig接口提供的方法,可获取Servlet名称、上下文,参数,获得所有的参数组成的Enumeration
public String getServletName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration<String> getInitParameterNames();
 通过web.xml获取配置参数的哦方式在Struts1中用到了,ActionServlet获取配置文件
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>
          /WEB-INF/struts-config.xml,
          /WEB-INF/struts-config-Wildcard.xml
       </param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics