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等

@Overridepublic 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多平台公布