j2ee_servlet

阅读的目的?我想要从中获得什么?

​ servlet规范相关 , 理解tomcat 和dispatchServlet实现

servlet 3.1规范概览

What is servlet

A servlet is a JavaTM technology-based Web component, managed by a container, that generates dynamic content. 

From servlet 3.1

History

From wiki

Servlet Life Cycle

  • Loading and Instantiation

    When the servlet engine is started, needed servlet classes must be located by the
    servlet container(WEB-INF/lib)

  • Initialization

    The container initializes the servlet instance by calling the init method of the Servlet interface with a unique (per servlet declaration) object implementing the ServletConfig interface
    

(ServletConfig used by Servlet)

  • Request Handling

    After a servlet is properly initialized, the servlet container may use it to handle client
    requests.

  • End of Service

Servlet 继承结构

image-20181219221816845

ServletContext

​ The ServletContext interface defines a servlet’s view of the Web application within which the servlet is running. (web.xml) The Container Provider is responsible for providing an implementation of the ServletContext interface in the servlet container.

InitParameter
config 
    -Filter
    -Listenr
    -Servlet
Attribute
Resource
...

see : ApplicationContext、ApplicationContextFacade (tomcat)

Request

HttpServletRequest

  • HTTP Protocol Parameters

    ■ getParameter

    ■ getParameterNames

    ■ getParameterValues

    ■ getParameterMap

  • File upload

    content-type : multipart/form-data

  • Attributes

  • Headers

  • Request Path Elements

    requestURI = contextPath + servletPath + pathInfo

  • Path Translation Methods

    ■ ServletContext.getRealPath

    ■ HttpServletRequest.getPathTranslated

  • Non Blocking IO

    Non-blocking IO only works with async request processing in Servlets and Filters

  • Cookies

  • SSL

  • Internationalization

    Accept-Language : zh-cn

  • ■ getLocale
    ■ getLocales

  • Request data encoding

    The default encoding of a request the container uses to create the request reader and parse POST data must be “ISO-8859-1” if none has been specified by the client request.

  • Lifetime of the Request Object

    Each request object is valid only within the scope of a servlet’s service method, or within the scope of a filter’s doFilter method, unless the asynchronous processing is enabled for the component and the startAsync method is invoked on the request object.

Response

image-20181219221656097

Filter

what is Filter?

A filter is a reusable piece of code that can transform the content of HTTP requests,responses, and header information.

org.springframework.web.servlet.HandlerInterceptor

/
* <p>HandlerInterceptor is basically similar to a Servlet 2.3 Filter, but in
* contrast to the latter it just allows custom pre-processing with the option
* of prohibiting the execution of the handler itself, and custom post-processing.
* Filters are more powerful, for example they allow for exchanging the request
* and response objects that are handed down the chain. Note that a filter
* gets configured in web.xml, a HandlerInterceptor in the application context.
*/

Lifecycle Events

Event
    -Servlet
    -Session
    -Request
EventListener
    -Servlet
    -Session
    -Request

Example : ServletContextListener

public class ContextLoaderListener implements ServletContextListener {
    private ContextLoader contextLoader;

    public ContextLoaderListener() {
    }

    public void contextInitialized(ServletContextEvent event) {
        this.contextLoader = this.createContextLoader();
        this.contextLoader.initWebApplicationContext(event.getServletContext());
    }

    protected ContextLoader createContextLoader() {
        return new ContextLoader();
    }

    public ContextLoader getContextLoader() {
        return this.contextLoader;
    }

    public void contextDestroyed(ServletContextEvent event) {
        if(this.contextLoader != null) {
            this.contextLoader.closeWebApplicationContext(event.getServletContext());
        }

    }
}

Session

Servlet源码分析

Server + Applet 的缩写,表示一个服务器应用 , 以下内容为javax.servlet

Servlet接口

package javax.servlet;
import java.io.IOException;
public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
    
    public ServletConfig getServletConfig();
    
    public void service(ServletRequest req, ServletResponse res)
   throws ServletException, IOException;

    public String getServletInfo();
    
    public void destroy();
    
}

Load-on-startup 为负的话不会在容器启动调用

ServletConfig接口

package javax.servlet;
import java.util.Enumeration;
/**
 * A servlet configuration object used by a servlet container
 * to pass information to a servlet during initialization. 
 */
 public interface ServletConfig {
    
    public String getServletName();

    public ServletContext getServletContext();

    public String getInitParameter(String name);

    public Enumeration<String> getInitParameterNames();

}

如下配置

<!--web.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>application-context.xml</param-value>
    </context-param>
<servlet>
    <servlet-name>demoDispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.Dispatcher</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>demo-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
</web-app>

在Servlet中 可以分别通过它们的getInitParameter方法获取,比如:

String contextLocation = getServletConfig().getServletContext().getInitParameter(
    "contextConfigLocation");
String servletLocation = getServletConfig().getInitParameter("contextConfigLocation");

GenerieServlet

`Servlet`的默认实现,同时实现了`ServletConfig`接口、`Serializable`接口,所以可以直接调用`ServletConfig`里面的方法。详细可参考如下类注释。
package javax.servlet;

import java.io.IOException;
import java.util.Enumeration;

/**
 *
 * Defines a generic, protocol-independent
 * servlet. To write an HTTP servlet for use on the
 * Web, extend {@link javax.servlet.http.HttpServlet} instead.
 *
 * <p><code>GenericServlet</code> implements the <code>Servlet</code>
 * and <code>ServletConfig</code> interfaces. <code>GenericServlet</code>
 * may be directly extended by a servlet, although it's more common to extend
 * a protocol-specific subclass such as <code>HttpServlet</code>.
 *
 * <p><code>GenericServlet</code> makes writing servlets
 * easier. It provides simple versions of the lifecycle methods 
 * <code>init</code> and <code>destroy</code> and of the methods 
 * in the <code>ServletConfig</code> interface. <code>GenericServlet</code>
 * also implements the <code>log</code> method, declared in the
 * <code>ServletContext</code> interface. 
 *
 * <p>To write a generic servlet, you need only
 * override the abstract <code>service</code> method. 
 *
 */
public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    private transient ServletConfig config;

    public GenericServlet() {}

    public void destroy() {}
    
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }
    
    public Enumeration getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }   
    
    public ServletConfig getServletConfig() {
        return config;
    }
    
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }
    
    public String getServletInfo() {
        return "";
    }

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

    public void init() throws ServletException {
    }
    
    public void log(String msg) {
        getServletContext().log(getServletName() + ": "+ msg);
    }
   
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }
    
    public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
    
    public String getServletName() {
        return config.getServletName();
    }
}

附:为什么需要实现 java.io.Serializable 接口?

答:在 Servlet 2.4 规范的 7.7.2 Distributed Environments 章节中,有一句这样的描述:

The distributed servlet container must support the mechanism necessary for
migrating objects that implement Serializable.

按照规范的设计,Servlet 有一个钝化的特性,类似于 Servlet 持久化到文件,然后当容器 Crash 回复后,可以重新恢复保存前的状态。

HttpServlet

package javax.servlet.http;

import ....;

/**
 * Provides an abstract class to be subclassed to create
 * an HTTP servlet suitable for a Web site. A subclass of
 * <code>HttpServlet</code> must override at least 
 * one method, usually one of these:
 *
 * <ul>
 * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
 * <li> <code>doPost</code>, for HTTP POST requests
 * <li> <code>doPut</code>, for HTTP PUT requests
 * <li> <code>doDelete</code>, for HTTP DELETE requests
 * <li> <code>init</code> and <code>destroy</code>, 
 * to manage resources that are held for the life of the servlet
 * <li> <code>getServletInfo</code>, which the servlet uses to
 * provide information about itself 
 * </ul>
 *
 * <p>There's almost no reason to override the <code>service</code>
 * method. <code>service</code> handles standard HTTP
 * requests by dispatching them to the handler methods
 * for each HTTP request type (the <code>do</code><i>XXX</i>
 * methods listed above).
 *
 * <p>Likewise, there's almost no reason to override the 
 * <code>doOptions</code> and <code>doTrace</code> methods.
 *
 */
public abstract class HttpServlet extends GenericServlet
    implements java.io.Serializable
{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    
    /**
    * Resource bundles contain locale-specific objects.
    */
    private static final String LSTRING_FILE =
    "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
    ResourceBundle.getBundle(LSTRING_FILE);

    public HttpServlet() { }
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

    protected long getLastModified(HttpServletRequest req) {
        return -1;
    }

    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
        NoBodyResponse response = new NoBodyResponse(resp);

        doGet(req, response);
        response.setContentLength();
    }
  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
  
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
       //略,类似doPost
    }
    
    protected void doDelete(HttpServletRequest req,
                HttpServletResponse resp)
    throws ServletException, IOException{
       //略,类似doPost
    }
  
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
      //略,主要用于调试,输出允许类型
    }
    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);
        }
    }
    
    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);
    }
}

/*
 * A response that includes no body, for use in (dumb) "HEAD" support.
 * This just swallows that body, counting the bytes in order to set
 * the content length appropriately.  All other methods delegate directly
 * to the HTTP Servlet Response object used to construct this one.
 */
// file private
class NoBodyResponse extends HttpServletResponseWrapper {
    private NoBodyOutputStream		noBody;
    private PrintWriter			writer;
    private boolean			didSetContentLength;

    // file private
    NoBodyResponse(HttpServletResponse r) {
        super(r);
    noBody = new NoBodyOutputStream();
    }
    // ....
}
/*
 * Servlet output stream that gobbles up all its data.
 */
 
// file private
class NoBodyOutputStream extends ServletOutputStream {
    //...
}

doXXX 都是模板方法,如果子类没有实现将抛出异常

doGet 方法前还会对是否过期做检查,如果没有过期,则直接返回304状态码做缓存。

doHead调用了doGet的请求,然后返回空body的response

doOptions和doTrace 主要是用来做一些调试工作


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com

文章标题:j2ee_servlet

字数:2.3k

本文作者:zhengyumin

发布时间:2019-02-02, 18:05:54

最后更新:2020-01-25, 14:59:58

原始链接:http://zyumin.github.io/2019/02/02/Spec_Servlet/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。