天天看點

servletServletServletConfigGenericServletServletContextservlet啟動過程servlet初始化配置參數

servlet相關類包括Servlet、ServletConfig、ServletContext、GenericServlet、HttpServlet等。

Servlet

Servlet定義了所有servlet必須實作的功能,每一個Servlet都是運作在web服務中的一個小的java程式,用于接收并響應來自web用戶端的請求。

Servlet提供了最基本的管理生命周期接口:init、service、destroy:

init:初始化,僅被servlet容器調用一次,執行成功則表明可以用于投入使用;

service:具體服務接口,被servlet容器調用;

destroy:銷毀,在此接口中可以釋放保持的資源,被servlet容器調用。           

源碼及其主要doc如下:

/**
 * Defines methods that all servlets must implement.
 *
 * <p>A servlet is a small Java program that runs within a Web server.
 * Servlets receive and respond to requests from Web clients,
 * usually across HTTP, the HyperText Transfer Protocol. 
 *
 * <p>To implement this interface, you can write a generic servlet
 * that extends <code>javax.servlet.GenericServlet</code> or an HTTP servlet that
 * extends <code>javax.servlet.http.HttpServlet</code>.
 *
 * @see     GenericServlet
 * @see     javax.servlet.http.HttpServlet
 */
public interface Servlet {

    /**
     * Called by the servlet container to indicate to a servlet that the 
     * servlet is being placed into service.
     *
     * <p>The servlet container calls the <code>init</code>
     * method exactly once after instantiating the servlet.
     * The <code>init</code> method must complete successfully
     * before the servlet can receive any requests.
     * ...
     */
    public void init(ServletConfig config) throws ServletException;
    
    /**
     *
     * Returns a {@link ServletConfig} object, which contains
     * initialization and startup parameters for this servlet.
     * The <code>ServletConfig</code> object returned is the one 
     * passed to the <code>init</code> method. 
     * ...
     */
    public ServletConfig getServletConfig();
    
    /**
     * Called by the servlet container to allow the servlet to respond to a request.
     * ...
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
    
    ...

    /**
     * Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.  This method is
     * only called once all threads within the servlet's <code>service</code> method have exited or after a timeout
     * period has passed. After the servlet container calls this method, it will not call the <code>service</code> method again
     * on this servlet.
     *
     * <p>This method gives the servlet an opportunity to clean up any resources that are being held (for example, memory,
     * file handles, threads) and make sure that any persistent state is synchronized with the servlet's current state in memory.
     */
    public void destroy();
}           

ServletConfig

ServletConfig是屬于一個具體的servlet的配置資訊,在servlet初始化時由servlet容器傳遞給servlet。

ServletConfig提供了servlet的基本配置資訊,如servlet名稱、ServletContext、初始化參數等:

getServletName: 擷取名稱;

getInitParameter:擷取初始化參數;

getServletContext:擷取Servlet上下文。           
/**
 * A servlet configuration object used by a servlet container
 * to pass information to a servlet during initialization. 
 */
 public interface ServletConfig {
    
    /**
     * Returns the name of this servlet instance.
     */
    public String getServletName();


    /**
     * Returns a reference to the {@link ServletContext} in which the caller is executing.
     */
    public ServletContext getServletContext();

    
    /**
     * Gets the value of the initialization parameter with the given name.
     */
    public String getInitParameter(String name);


    /**
     * Returns the names of the servlet's initialization parameters
     * as an <code>Enumeration</code> of <code>String</code> objects, 
     * or an empty <code>Enumeration</code> if the servlet has
     * no initialization parameters.
     */
    public Enumeration<String> getInitParameterNames();
}           

GenericServlet

GenericServlet定義了通用的、與協定無關的servlet規範,實作了Servlet和ServletConfig接口,并且所有具體的servlet類都要繼承于該類。

如果需要與協定相關的servlet,例如http協定,可以擴充其子類HttpServlet。

ServletContext

ServletContext定義了一系列方法,用于servlet和其所在的servlet容器之間進行互動。例如擷取檔案的mime類型,分發請求等。

ServletContext中包含初始化環境參數,初始化配置參數專屬于某一個指定servlet,而初始化環境參數是所有servlet中共享的資料。@WebServlet用于指定servlet配置,是以不能設定初始化環境參數,是以隻能在web.xml中使用标簽進行定義,或者在監聽器中使用ServletContext的setInitParameter方法:

/**
     * Sets the context initialization parameter with the given name and
     * value on this ServletContext.
     */
    public boolean setInitParameter(String name, String value);           

擷取初始化環境參數可以使用ServletContext的getInitParameter方法:

/**
     * Returns a <code>String</code> containing the value of the named
     * context-wide initialization parameter, or <code>null</code> if the 
     * parameter does not exist.
     *
     * <p>This method can make available configuration information useful
     * to an entire web application.  For example, it can provide a 
     * webmaster's email address or the name of a system that holds 
     * critical data.
     */
    public String getInitParameter(String name);           

例如:

getServletContext().getInitParameter("className");           

ServletContext的getRequestDispatcher可以用來請求轉發:

/**
     * Returns a {@link RequestDispatcher} object that acts
     * as a wrapper for the resource located at the given path.
     * A <code>RequestDispatcher</code> object can be used to forward 
     * a request to the resource or to include the resource in a response.
     * The resource can be dynamic or static.
     *
     * <p>The pathname must begin with a <tt>/</tt> and is interpreted as
     * relative to the current context root.  Use <code>getContext</code>
     * to obtain a <code>RequestDispatcher</code> for resources in foreign
     * contexts.
     *
     * @param path     a <code>String</code> specifying the pathname
     *            to the resource
     */
    public RequestDispatcher getRequestDispatcher(String path);           

該方法傳回一個定位到指定資源的包裝對象,可以用來執行forward或include指令。

路徑名稱必須以“/”開頭,并被解析為相對于目前應用環境的根路徑,例如:

@RestController
@RequestMapping("/api/admins")
public class AdminResource {}           

"/api"中的"/"就是目前應用環境根路徑。

ServletContext還可以擷取資源路徑及内容,其getResourcePaths可以擷取指定路徑的子路徑,例如:

@WebServlet({"/test"})
public class TestServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void init(ServletConfig config) {
        Set<String> resourcePaths = config.getServletContext().getResourcePaths("/");
        resourcePaths.forEach(System.out::println);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    ...
}           

擷取到的内容為:

servletServletServletConfigGenericServletServletContextservlet啟動過程servlet初始化配置參數

傳回的如果是目錄則以“/”結尾,否則為檔案,其中“/swagger-ui.html”為檔案,其餘為目錄。

擷取指定目錄下的子目錄及檔案:

Set<String> ress = context.getResourcePaths("/WEB-INF");
ress.forEach(System.out::println);           

擷取指定檔案内容可以使用getResourceAsStream方法:

InputStream in = context.getResourceAsStream("/WEB-INF/index.html");           

servlet啟動過程

servlet生命周期如init、service和destroy都是由servlet容器控制的。

初始啟動時,servlet容器首先讀取web.xml的servlet配置資訊,并将資訊存入ServletConfig中,每一個servlet都會對應一個ServletConfig。

servlet容器調用servlet的構造函數構造Servlet對象,然後對servlet進行初始化,即調用servlet的init方法,并将該servlet的ServletConfig對象作為參數傳入,如下:

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

在運作期間,servlet接收并響應用戶端的請求,例如HTTPServlet中的service方法,在service執行時根據請求類型判斷具體執行方法doGet還是doPost、doHead等,如下:

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

最後通過destroy方法銷毀servlet并釋放其保持的連接配接。

servlet初始化配置參數

servlet初始化配置參數是專屬于某一個指定額servlet,可以由ServletConfig如下方法擷取:

/**
     * Gets the value of the initialization parameter with the given name.
     */
    public String getInitParameter(String name);           

其設定方式可以在web.xml中配置,使用标簽,其下屬性表示參數名,表示參數值:

<servlet>
        <servlet-name>UnifiedUser</servlet-name>
        <servlet-class>com.***.***.UnifiedUser</servlet-class>
        <init-param>
            <param-name>className</param-name>
            <param-value>com.***.***.impl.UnifiedUserService</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>UnifiedUser</servlet-name>
        <url-pattern>/UnifiedUser</url-pattern>
    </servlet-mapping>           

配置後,servlet容器初始化servlet時就會從web.xml中讀取其配置資訊并儲存在ServletConfig對象中,就可以通過getInitParameter方法擷取了。

另外,可以使用servlet注解設定初始參數:

@WebServlet(
    name="ServletConfigDemo",
    urlPatterns={"/conf"},
    initParams={
        @WebInitParam(name="className", value="com.***.***.impl.UnifiedUserService"),
        @WebInitParam(name="environment", value="dev")
    }
)           

需要注意的是,當web.xml中與@WebServlet中的name屬性值相同,web.xml的定義可以覆寫@WebServlet注解中的定義。

例如引用的jar包中class檔案定義了@WebServlet注解,但是我們需要修改注解中定義的值,這時候可以在web.xml檔案中做相同的servlet定義來覆寫注解的中值。