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 {
}
...
}
擷取到的内容為:
傳回的如果是目錄則以“/”結尾,否則為檔案,其中“/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定義來覆寫注解的中值。