servlet

  1. 云栖社区>
  2. 博客>
  3. 正文

servlet

~信~仰~ 2019-08-08 10:45:05 浏览202
展开阅读全文

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 {
    }
    ...
}

获取到的内容为:

image

返回的如果是目录则以“/”结尾,否则为文件,其中“/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定义来覆盖注解的中值。

网友评论

登录后评论
0/500
评论
~信~仰~
+ 关注