乐趣区

关于程序员:JavaWeb之Servlet接口

Servlet 接口

什么是 Servlet?

Servlet 是一种基于 Java 技术的 Web 组件,用于生成动静内容,由容器治理,是平台无关的 Java 类组成,并且由 Java Web 服务器加载执行,是 Web 容器的最根本组成单元

什么是 Servlet 容器?

Servlet 容器作为 Web 服务器或应用服务器的一部分,通过申请和响应提供 Web 客户端与 Servlets 交互的能力,容器治理 Servlet 实例以及它们的生命周期 (创立、初始化、提供服务、销毁等)

在 java web 中不论是应用 J2EE 原生的 servlet/jsp 还是应用 springmvc/springboot,在 web 服务器看来只是对外裸露进去的 Servlet,而这个 Servlet 是 javax.servlet.Servlet 接口,该接口定义了 Servlet 引擎与 Servlet 程序之间通信的协定约定。

// Servlet 的加载和实例化能够产生在容器启动时,也能够提早初始化直到有申请须要解决时
public interface Servlet {
      // 负责初始化 Servlet 对象,容器创立好 Servlet 对象后由容器调用调用,只执行一次
      // 当 load-on-startup 设置为正数或者不设置时会在 Servlet 第一次用到时才被调用
    void init(ServletConfig config) throws ServletException;
        // 获取该 Servlet 的初始化参数信息
    ServletConfig getServletConfig();
        // 负责响应客户端的申请,当容器接管到客户端要求拜访特定 Servlet 对象的申请时,会调用该 Servlet 对象的 service() 办法,每次申请都会执行
    void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
        // 返回 Servlet 信息,蕴含创建者、版本、版权等信息
    String getServletInfo();
        // Servlet 完结生命周期时调用,开释 Servlet 对象占用的资源
    void destroy();}

<!– more –>

而为了简化开发,jdk 中提供了一个实现 Servlet 接口的简略的 Servlet 类,javax.servlet.GenericServlet,该类实现了 Servlet 的基本功能,对 init(ServletConfig config)、service(ServletRequest req, ServletResponse res) 和 destroy() 办法提供了默认实现

jdk 针对 HTTP 协定专门提供了一个 Servlet 类,javax.servlet.http.HttpServlet,该类继承于 GenericServlet 类,在其根底上针对 HTTP 的特点进行裁减,个别编写程序时继承 HttpServlet 即可,这样只须要重写 doGet() 和 doPost() 办法即可

Servlet 中波及的次要对象

  • 申请对象 ServletRequest、HttpServletRequest
  • 响应对象 ServletResponse、HttpServletResponse
  • Servlet 配置对象 ServletConfig
  • Servlet 上下文对象 ServletConfig

Servlet 注册与运行

Servlet 编写好之后须要在 web.xml 中进行注册和映射能力被 Servlet 容器加载从而被外界拜访

<!-- 留神 servlet 和 servlet-mapping 都是成对呈现的 -->    
<!-- 注册 Servlet -->    
    <servlet>
        <servlet-name>HW</servlet-name>
        <servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
              <!-- 配置 servlet 初始化 init 时中 ServletConfig 参数 -->
        <init-param>
          <param-name>name</param-name>
          <param-value>john</param-value>
        </init-param>
        <!-- servlet 加载机会, 若为正数,则在第一次申请时被创立,若为 0 或者负数,在 web 利用被 Servlet 容器加载时创立实例,值越小越早被启动  -->
        <load-on-startup>1</load-on-startup>
    </servlet>
        <!-- 映射 Servlet -->
    <servlet-mapping>
      <!-- 对应 servlet 标签中的 servlet-name 值 -->
        <servlet-name>HW</servlet-name>
          <!-- 结尾的 / 示意 web 用用程序的根目录 -->
        <url-pattern>/HelloWorld</url-pattern>
    </servlet-mapping>

tomcat 中的 web.xml 蕴含有一个缺省的 Servlet

    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

ServletConfig

对于每个 Servlet 可能在启动时都须要一些初始化参数,而所有的 Servlet 是交由 Servlet 引擎去实例化的,那么也就是须要将每个 Servlet 的初始化参数也都配置到 web.xml 中,Servlet 引擎将 Servlet 容器对象和 Servlet 的配置信息封装到 ServletConfig 中,并在 Servlet 初始化时将 ServletConfig 传递给该 Servlet。javax.servlet.ServletConfig 接口的作用就是用来定义 ServletConfig 对象所须要对外提供的办法

public interface ServletConfig {
  // 获取 web.xml 中定义的 servlet-name
    String getServletName();
    // ServletContext 示意的是利用自身
    ServletContext getServletContext();
        // 获取 init-param 配置的参数
    String getInitParameter(String name);
        // 获取 init-param 配置的所有参数
    Enumeration<String> getInitParameterNames();}

Servlet 引擎装载并创立一个 Servlet 对象后,会调用该对象的 init(ServletConfig config) 办法,Servlet 中的 ServletConfig getServletConfig() 办法会返回 init 传入的 ServletConfig 对象

    <servlet>
        <servlet-name>HW</servlet-name>
        <servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
        <init-param>
            <param-name>name</param-name>
            <param-value>zhanghe</param-value>
        </init-param>
    </servlet>
// 获取指定的属性
config.getInitParameter("name")
// 获取所有的属性
config.getInitParameters()

ServletContext

每个 Web 应用程序在启动时都会创立一个 ServletContext 对象,每个 Web 应用程序都有一个惟一的 ServletContext 对象,javax.servlet.ServletContext 接口定义了 ServletContext 须要对外提供的办法,Servlet 通过这些办法来和 ServletContext 容器进行通信

  • 该对象代表以后 WEB 利用,能够获取到 web 利用的信息,一个 Web 利用只有一个 ServletContext 对象
  • 能够应用 ServletConfig 的 getServletContext() 获取到 ServletContext
  • 能够获取 web 利用的初始化参数,这是全局的办法,在 web.xml 中配置 <context-param>
  • 获取 web 利用某个文件的绝对路径 (在服务器上的门路,不是部署前的办法) getRealPath
  • 获取以后利用的名称 getContextPath
  • 获取以后 web 利用的某一个文件对应的输出流 getResourceAsStream() path 是绝对于以后 web 利用的根目录
    <context-param>
        <param-name>email</param-name>
        <param-value>master@163.com</param-value>
    </context-param>

servlet 生命周期

生命周期相干办法,servlet 生命周期中的办法全是由 servlet 容器来调用的

  • 结构器 web 容器调用 Servlet 的无参结构器,默认是在第一次申请时被加载,能够通过 load-on-startup 标签来进行设置什么时候加载
  • init 办法
  • service 办法
  • destory 办法

init 办法 – 初始化

init 办法在第一次创立 servlet 时被调用,在后续每次申请时都不会被调用。

当用户调用 servlet 的时候,该 servlet 的一个实例就会被创立,并且为每一个用户产生一个新的线程,init() 用于进行一些初始化数据的加载和解决,这些数据会被用于 servlet 的整个生命周期

void init(ServletConfig config) throws ServletException;

为了避免重写该办法时开发者遗记将入参 config 赋值给成员变量 config,故而提供了 GenericServlet 类进行了一次封装

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

在进行 Servlet 重写时只须要重写不带参数的 init 办法即可

public void init() throws ServletException

该办法是由 servlet 容器调用的

什么时候触发初始化

有两种状况会进行 Servlet 的初始化

  • Servlet 被客户端首次申请拜访时触发初始化办法
  • 如果配置了 load-on-startup 元素,则在 Servlet 容器启动该 Servlet 所属 Web 利用时就会初始化该 Servlet

       <servlet>
           <servlet-name>dispatcherServlet</servlet-name>
           <servlet-class>
               org.springframework.web.servlet.DispatcherServlet
           </servlet-class>
           <load-on-startup>1</load-on-startup>
       </servlet>
重写 init 办法

GenericServlet 实现了 Servlet 和 ServletConfig,是一个抽象类,并对 init(ServletConfig var1) 办法进行了一层封装,有一个 ServletConfig 成员变量,在 init() 办法中进行了初始化,使得能够间接在 GenericServlet 中间接应用 ServletConfig 办法

而咱们平时写 Servlet 大多是继承于 HttpServlet 类的,在对 init 办法进行重写时, 重写不带参的 init() 办法即可

//GenericServlet 类

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

public void init() throws ServletException {}

该办法由 GenericServlet 的调用,如果须要应用到 ServletConfig 则调用 getServletConfig() 办法来获取

service 办法

service 办法是理论解决申请的办法,servlet 容器调用 service 办法来解决申请,并将响应写回到客户端,每次服务器接管到一个新的 servlet 申请时,服务器会产生一个新的线程来调用服务

void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
解决申请逻辑

HttpServlet 继承了 GenericServlet,重写了 service 办法,将 ServletRequest 和 ServletResponse 转换为 HttpServletRequest 和 HttpServletResponse,并依据不同的申请形式进行散发,doGet/doPost/doHead 等

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    // 如果申请类型不相符,则抛出异样
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {throw new ServletException("non-HTTP request or response");
    }
        // 转换成 Http 的 request 和 response
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
        // 进行 http 的解决办法
    service(request, response);
}

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

servlet 能够在任何协定下拜访 , 写的 Servlet 必须实现 Servlet 接口, 在 http 协定下能够应用 HttpServlet,用来给 Web 拜访的

在 HttpServlet 类中对于 service() 办法进行了解决,依据申请形式的不同,将申请散发到了不同的办法,而咱们个别状况下写 Servlet 也是继承自 HttpServlet 的,所以在写申请解决逻辑时,只须要重写 doGet() 和 doPost() 办法即可

// HttpServlet 类

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String msg = lStrings.getString("http.method_get_not_supported");
    this.sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String msg = lStrings.getString("http.method_post_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
}
GET 办法

GET 办法时浏览器向 web 服务器传递信息的默认办法,会在地址栏上产生很长的字符串,且 GET 办法有大小限度,申请字符串中最多只能有 1024 个字符

POST 办法

POST 办法不将申请信息放到地址中

destroy 办法

destory() 办法只在 servlet 生命周期完结时被调用一次。能够在 destory() 办法中进行一些资源的清理,如敞开数据库连贯、进行后盾线程等

https://zhhll.icu/2021/javaweb/ 根底 /1.Servlet 接口 /

本文由 mdnice 多平台公布

退出移动版