乐趣区

关于后端:这些Servlet知识你一定要知道金九银十大厂面试官都爱问

前言

 Servlet 是服务器端的 Java 应用程序,能够生产动静 Web 页面。透过 JSP 执行过程能够晓得 JSP 最终被编译成一个.class 文件,查看该文件对应的 Java 类,发现该 Java 类继承自 org.apache.jasper.runtime.HttpJspBase 类,而 HttpJspBase 继承自 HttpServlet 类,由此可知 JSP 第一次运行时本质上是被 JSP 引擎翻译成了一个 Servlet,而后再编译,最初再执行。

自定义 Servlet 类继 承 HttpServlet 抽象类,HttpServlet 抽象类继承自 GenericServlet 抽象类,GenericServlet 抽象类实现了 Servlet、ServletConfig 和 Serializable 接口

Servlet 申明周期:

1、加载及实例化

Servlet 容器负责加载和实例化 Servlet。当客户端第一次 (在 web.xml 文件中,通过 load-on-startup 标签能够配置 Servlet,当 web 我的项目公布后立刻创立 Servlet 实例)  给服务器发送该 Servlet 申请时,Servlet 容器会加载并创立 Servlet 实例,(留神:默认状况下不是 Tomcat 服务器或服务器上的 Web 利用启动的时候加载并实例化 Servlet)。当客户端(能够是非第一次申请的客户端)再次向服务器发送该 Servlet 申请时,服务器会从内存中查找该 Servlet 实例,并用找到的 Servlet 实例解决用户申请。

在该过程中,Servlet 容器会创立一个 ServletConfig 对象,该对象蕴含了 Servlet 的初始化配置信息。依据用户申请的 URL 地址,Servlet 容器会依据配置信息查找该申请对应的 Servlet 类,由容器创立并治理该 Servlet。

2、初始化

在 Servlet 容器实现 Servlet 类的实例化操作后,Servlet 容器会调用 Servlet 的 init() 办法(在 javax.servelt.Servlet 接口中定义)对该 Servlet 进行初始化。对于每一个 Servlet 实例来说,init()办法只会被调用一次。初始化的目标是让 Servlet 在解决用户申请之前,做一些必要的筹备工作,例如建设数据库连贯,援用其余资源等。

3、解决申请

Servlet 初始化之后,就处于就绪状态期待接管用户申请。当 Servlet 容器接管到客户端针对该 Servlet 的申请后,首先会针对这个申请创立 ServletRequest 和 ServletResponse 对象,之后调用 Servlet 的 service()办法并把这两个参数传递给 service()办法解决客户端申请。Servlet 实例通过 ServletRequest 对象取得客户端的申请,通过调用 ServletResponse 对象的办法进行响应。申请处理完毕,ServletRequest 和 ServletResponse 对象被销毁。

不论客户端发送申请的形式是 Get 还是 POST,这个申请都由 service 办法来解决。在 service 办法的处理过程中,会依据客户端发送申请的形式不同,调用 doGet 和 doPost 办法别离进行解决,通过 HttpServlet 类中的 service 办法能够理解这一调用过程,如下代码:

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) {doGet(req, resp);
            } else {long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {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 {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);
        }
    }

4、销毁

销毁 Servlet 由 Servlet 容器实现。默认状况下,用户第一次发送 Servlet 申请,该 Servlet 加载、实例化、初始化、解决用户申请,当申请处理完毕后,该 Servlet 通常状况下驻留在内存中,期待解决下一个针对该 Servlet 的申请。当下一个针对该 Servlet 的申请达到时,间接从内存中获取该 Servlet 实例并对该申请进行解决。如果 Tomcat 这个 Web 应用服务器敞开(服务器上所有的 Web 利用都敞开),或者该 Servlet 所在的 Web 利用敞开,该 Servlet 实例会被销毁。

Web 利用被敞开时,Servlet 容器会先调用 Servlet 实例的 destroy 办法,而后再销毁 Servlet 实例,同时也会销毁与 Servlet 相关联的 ServletConfig 对象。程序员通常在 destroy() 办法的实现中开释该 Servlet 所占用的资源,如敞开数据库连贯,敞开文件输出 / 输入流等。

通过 Servlet 申明周期能够晓得所创立的 Servlet 对象属于单例。

Servlet2.X 配置

在 web.xml 文件中,通过在 <web-app> 节点下配置 servlet 元素和 servlet-mapping 元素,把用户拜访的 URL 映射到指定的 Servlet 类,如下代码:

<web-app>
  <!-- 省略其余配置 -->
  <servlet>
      <!-- servlet-name 指定 Servlet 名,要与上面 servlet-mapping 元素中的 servlet-name 保持一致 -->
    <servlet-name>doLogin</servlet-name>
    <!-- servlet-class 对应着 Servlet 类齐全限定名 -->
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
      <!-- servlet-name 要与下面 servlet 元素中的 servlet-name 保持一致 -->
    <servlet-name>doLogin</servlet-name>
    <!-- url-pattern 设定以后 Servlet 在浏览器中运行时的 url -->
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>
</web-app>

下面采纳了准确匹配的模式配置了 URL 到 Servlet 之间的映射关系,接下来介绍两种非准确匹配的 Servlet 配置形式:

<!-- 对 doLogin 门路下的所有申请都由 doLogin 对应的 Servlet 类进行解决 -->
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin/*</url-pattern>
  </servlet-mapping>
  
  <!-- 对所有以.do 为后缀的申请都由 doLogin 对应的 Servlet 类进行解决 -->
    <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <!-- 不能为 /*.do -->
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

在配置了 URL 与 Servlet 的映射后,当 Servlet 容器收到一个申请时,首先确定是由哪个 Web 利用响应这个申请,而后从该 Web 利用的 web.xml 文件中查找 URL 对应的 Servlet 类进行解决。

Servlet 初始化参数设置

在 web.xml 文件中配置 Servlet 时,还能够在 servlet 元素中增加 init-param 元素事后对 Servlet 进行初始化设置,当 Servlet 加载时即可从该 Servlet 配置文件中获取初始化参数。

  <!-- 省略其余配置 -->
  <servlet>
    <servlet-name>doLogin</servlet-name>
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
    <!-- 配置多个初始化参数,则须要写多个 init-param 元素 -->
    <init-param>
            <param-name>name</param-name>
            <param-value>Tom</param-value>
    </init-param>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>

如何获取:

a、在无参 init 办法中间接调用 getInitParameter(String name) 办法即可,如下代码:

@Override
public void init() throws ServletException {String name = getInitParameter("name");
    System.out.println(name);
}

b、在参数为 ServletConfig 的办法中调用 ServletConfig 内 getInitParameter(String name) 办法即可,如下代码:

@Override
public void init(ServletConfig config) throws ServletException {String name = config.getInitParameter("name");
    System.out.println(name);
}

Servlet 上下文(环境对象)初始化参数设置

有时候不仅须要针对单个 Servlet 进行初始化参数设置,还须要对蕴含该 Web 援用中所有 Servlet 的环境对象进行初始化参数设置,使该参数能被所有的 Servlet 共享,如下代码:

  <!-- 省略其余配置 -->
  <!-- 配置多个初始化参数,则须要写多个 context-param 元素 -->
   <context-param>
       <param-name>name</param-name>
       <param-value>Tom</param-value>
   </context-param>
 
  <servlet>
    <servlet-name>doLogin</servlet-name>
    <servlet-class>com.jd.serlvet.LoginServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>doLogin</servlet-name>
    <url-pattern>/doLogin</url-pattern>
  </servlet-mapping>
</web-app>

如何获取:

a、在无参 init 办法中间接调用 getServletContext () 办法获取 Servlet 上下文对象,而后应用该对象调用 getInitParameter 办法即可,如下代码:

@Override
public void init() throws ServletException {String name = getServletContext ().getInitParameter("name");
    System.out.println(name);
}

b、在参数为 ServletConfig 的办法中调用 ServletConfig 内 getServletContext () 办法获取 Servlet 上下文对象,而后应用该对象调用 getInitParameter 办法即可,如下代码:

@Override
public void init(ServletConfig config) throws ServletException {ServletContext servletContext = config.getServletContext();
    String name = servletContext.getInitParameter("name");
    System.out.println(name);
}

Servlet 3.0

Servlet API 蕴含 javax.servlet 和 javax.servlet.http 两个包,从 Servlet 3.0 开始,为了实现 Servlet3.0 的一些新个性,又减少了 javax.sevlet.annotation 和 javax.sevlet.descriptor 两个包,Tomcat 服务器必须是 7.0 及其以上版本

Servlet3.0 的重大变革之一是反对注解,通过应用注解定义并部署 Servlet,程序员毋庸在 web.xml 文件中配置 Servlet,如下代码:

@WebServlet(name="doLogin",urlPatterns="/doLogin",initParams={@WebInitParam(name="name",value="Tom")})
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {response.sendRedirect("success.jsp");
    }
}

 阐明:

name 属性:指定 Servlet 名,相似于 web.xml 中 servlet-name 元素;

urlPatterns 属性:指定拜访 URL,相似于 web.xml 中的 url-pattern 元素;能够是数组,URL 之间应用逗号距离。

initParams 属性:设置初始化参数,该属性中应用 @WebInitParam 注解设置单个初始化参数;在 @WebInitParam 注解中,name 属性指定参数名,相似于 web.xml 中的 param-name 元素;value 属性指定参数值,相似于 web.xml 中的 param-value 元素。能够在 initParams 属性中配置多个 @WebInitParam 注解,用于初始化多个参数。

wb.xml:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
 
<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <context-param>
          <param-name>name</param-name>
          <param-value>lucy</param-value>
  </context-param>
  <servlet>
      <servlet-name>TestServlet</servlet-name>
      <display-name>TestServlet</display-name>
      <description></description>
      <servlet-class>com.jd.servlet.TestServlet</servlet-class>
      <init-param>
          <param-name>mobile</param-name>
          <param-value>120</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
      <servlet-name>TestServlet</servlet-name>
      <url-pattern>/TestServlet</url-pattern>
  </servlet-mapping>
</web-app>

TestServlet.java:

package com.jd.servlet;
 
import java.io.IOException;
 
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/**
 * Servlet implementation class TestServlet
 */
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       //Servlet 对象属于单实例(程序运行完结之前,缓存中只有一个对象存在)public TestServlet() {
        // 用于为成员变量赋值,会触发对象创立:默认状况下第一次应用该 Servlet 时创建对象执行;
        //web.xml 中退出 <load-on-startup>1</load-on-startup>,在 tomcat 启动时对象创立,TestServlet 执行,随之 init()办法也立刻执行
        
        super();
        System.out.println("TestServlet"+this);
       
    }
 
    //
    @Override
    public void destroy() {super.destroy();
         System.out.println("destroy"+this);
    }
 
 
    @Override
    public void init() throws ServletException {// 初始化; 对于每一个 Servlet 实例来说,创建对象执行 init()办法,并且只会被调用一次
        super.init();
        String name=getServletContext().getInitParameter("name");// 调用公共参数
        System.out.println("1111111"+name);
        System.out.println("init())"+this);
    }
    
 
    @Override
    public void init(ServletConfig config) throws ServletException {super.init(config);
        String name=config.getServletContext().getInitParameter("name");// 调用公共参数
        System.out.println("你好"+name);
        String mobile=config.getInitParameter("mobile");// 调用私人参数
        System.out.println("快打"+mobile);
        System.out.println("init(config))"+this);
    }
 
    
    
    // 申请最先达到 service 判断执行哪个办法,doGet 还是 doPost
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.service(req, resp);
        System.out.println("service"+this);
    }
 
    // a 标签,form 表单 method 是 get 办法   异步默认 type 是 get 办法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("doGet"+this);
    }
    //form 表单 method 是 post 办法  异步指定 type 是 post 办法
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("doPost"+this);
    }
 
}

最初

感激你看到这里,看完有什么的不懂的能够在评论区问我,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享 java 相干技术文章或行业资讯,欢送大家关注和转发文章!

退出移动版