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上下文。
源码及其主要doc如下:
/**
* 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定义来覆盖注解的中值。