Skip to content

3.Servlet

3.1 Servlet介绍

Servlet是基于Java语言的Web服务器端编程技术,按照Java EE规范定义,Servlet是运行在Servlet容器中的Java类,它能处理Web客户的HTTP请求,并产生HTTP响应。

  • Servlet对请求的处理和响应过程分为如下几个步骤:
    • 接收HTTP请求;
    • 取得请求信息,包括请求头和请求参数数据;
    • 调用其他Java类方法,完成具体的业务功能;
    • 实现到其他Web组件的跳转(包括重定向或请求转发);
    • 生成HTTP响应(包括HTML或非HTML响应)。

3.2 创建Servlet

  • 创建类继承自HttpServlet
    • 添加maven支持 servlet-api、 jsp-api
    • 在pom.xml 的dependencies
xml
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.0.1</version>
      <!--作用域  provided  参与编译,运行时抛弃 -->
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.1</version>
      <scope>provided</scope>
    </dependency>
  • 重写doGet或者doPost发方法
java
package com.neuedu.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 项目:      javaweb1206
 * 类名:       FirstServlet
 * 创建时间:  2020/12/6  13:46
 * 描述 :     第一个Servlet
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class FirstServlet extends HttpServlet {


    /**
     * 接受的 GET请求
     *     1 浏览器地址栏直接访问
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }


    /**
     * 接受POST请求
     * @param request   请求对象
     * @param response  响应对象
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        ServletOutputStream os = response.getOutputStream();

        os.write("hello servlet".getBytes());
        os.flush();
        os.close();

    }
}
  • 声明配置Servlet
    • 2.5版本规范中采用web.xml配置文件声明配置
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>

  <servlet>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>com.neuedu.servlet.FirstServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <url-pattern>/first</url-pattern>
  </servlet-mapping>

</web-app>
  • 3.x版本以上规范中使用注解声明配置
java
package com.neuedu.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 项目:      javaweb1206
 * 类名:       ${NAME}
 * 创建时间:  2020/12/6  14:42
 * 描述 :     ${dc}
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 *
 *
 *    http://127.0.0.1:8080/web/second
 *
 *
 */
@WebServlet(name = "SecondServlet",urlPatterns="/second")
public class SecondServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //响应浏览器一个内容
        ServletOutputStream os = resp.getOutputStream();
        os.write("Second servlet".getBytes());
        os.flush();
        os.close();
    }
}
  • 部署运行Servlet

    • 添加Tomcat的 jvm

    • sh
      -Dfile.encoding=utf-8

3.3 Servlet体系结构

Servlet是使用Servlet API(应用程序设计接口)及相关类和方法的Java程序。

Servlet API包含两个软件包:

  • javax.servlet包
    • 定义了所有Servlet类都必须实现或继承的通用接口和类
  • javax.servlet.http包
    • 定义了采用HTTP协议通信的HttpServlet类

3.3.1 Servlet接口

Servlet接口规定了必须由Servlet类实现并且由Servlet引擎识别和管理的方法集;

Servlet接口的基本目标是提供与Servlet生命周期相关的方法,如:init()、service()和destroy()等。

方法:

方法名称方法描述
init(ServletConfig config)Servlet初始化方法,在Servlet实例化后,容器调用该方法进行Servlet的初始化,init()方法只能被调用一次,如果此方法没有正常结束,就会抛出一个ServletException异常,一旦抛出该异常,Servlet将不再执行,随后对其进行再次调用会,容器会重新载入并再次运行init()方法。
service(ServletRequest req,ServletResponse resp)Servlet的服务方法。当用户对Servlet发出请求时容器会调用该方法处理用户的请求
destroy()Servlet的销毁方法。容器在终止Servlet服务前调用此方法,容器调用此方法前必须给service()线程足够时间来结束执行,因此接口规定当service()正在执行时,destroy()不被执行
getServletConfig()此方法可以让Servlet在任何时候获得ServletConfig对象
getServletInfo()此方法返回一个String对象,该对象包含Servlet的信息,例如:开发者、创建日期、描述信息等。该方法也可用于容器

3.3.2 GenericServlet抽象类

GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。

继承自GenericServlet的Servlet应该要覆盖service()方法。

常用方法:

方法名称功能描述
public void init(ServletConfig config)调用Servlet接口中的init()方法。此方法还有一无参的重载方法,其功能与此方法相同
public String getInitParameter(Stringname)返回名称为name的初始化参数的值
public ServletContext getServletContext()返回ServletContext对象的引用

3.3.3 HttpServlet抽象类

HttpServlet指能够处理HTTP请求的Servlet,它在原有Servlet接口上添加了对HTTP协议的处理,它比Servlet接口的功能更为强大。

HttpServlet类中的主要方法及描述:

方法方法描述
service(HttpServletRequest req,HttpServletResponse resp)HttpServlet在实现Servlet接口时,重写了service()方法,该方法会自动判断用户的请求方式;若为GET请求,则调用HttpServlet的doGet()方法;若为POST请求,则调用doPost()方法。因此,开发人员在编写Servlet时,通常只需要重写doGet()或doPost()方法,而不要去重写service方法。如果Servlet收到一个HTTP请求而你没有重载相应的do方法,它就返回一个说明此方法对本资源不可用的标准HTTP错误。
doGet(HttpServletRequest req,HttpServletResponse resp)此方法被本类的service()方法调用,用来处理一个HTTP GET 操作。
doPost(HttpServletRequest req,HttpServletResponse resp)此方法被本类的service()方法调用,用来处理一个HTTP POST 操作。

HttpServlet作为HTTP请求的分发器,除了提供对GET和POST请求的处理外,对于其他请求类型也提供了相应的处理方法(如:doHead()、doOptions()、doDelete()、doPut()、doTrace())

3.4 Servlet声明配置

Servlet的声明配置信息主要包括Servlet的描述、名称、初始参数、类路径以及访问地址等。

3.4.1 注解式声明

注解@WebServlet会在程序部署时被Servlet容器处理,容器将根据具体的属性配置把相应的类部署为Servlet;

注解@WebServlet的属性及描述

属性名类型描述
nameString指定Servlet的名字,可以为任何字符串,一般与Servlet的类名相同,如果没有显式指定,则该Servlet的取值即为类的全限定名
urlPatternsString[]指定一组Servlet的URL匹配模式,可以是匹配地址映射(如:/SimpleServlet)、匹配目录映射(如:/servlet/)和匹配扩展名映射(如:.action)
valueString[]该属性等价于urlPatterns属性。两个属性不能同时使用
loadOnStartupint指定Servlet的加载顺序。当此选项没有指定时,表示容器在该Servlet第一次被请求时才加载;当值为0或者大于0时,表示容器在应用启动时就加载这个Servlet。值越小,启动该servlet的优先级越高。原则上不同的Servlet应该使用不同的启动顺序数字
initParamsWebInitParam[]指定一组Servlet初始化参数,为可选项
asyncSupportedboolean声明Servlet是否支持异步操作模式,默认为false
descriptionString指定该Servlet的描述信息
displayNameString指定该Servlet的显示名,通常配合工具使用
  • Servlet的映射路径

    urlPatterns= {"/hello","/hello.do","/hello/hello.html"}

urlPatterns= {"/hello","*.do"}*

  • 精确匹配: /hello
    访问路径:http://localhost:8080/HelloServlet/hello /hello/hello.do 访问路径:http://localhost:8080/HelloServlet/hello/hello.do
  • 模糊匹配: /* 访问路径:http://localhost:8080/HelloServlet/任意路径 /hello/*
    访问路径: http://localhost:8080/HelloServlet/hello/任意路径.do、.action、*.后缀名
    访问路径: http://localhost:8080/HelloServlet/任意路径.do

3.4.2 Servlet2.5版本声明配置

​ 在web.xml中通过<servlet> </servlet>元素声明Servlet,其子元素及其描述如下表:

属性名类型描述
<description>String指定该Servlet的描述信息,等价于@WebServlet 的description属性
<display-name>String指定该Servlet的显示名,通常配合工具使用,等价于@WebServlet的displayName属性
<servlet-name>String指定Servlet的名称,一般与Servlet的类名相同,要求在一个web.xml文件内名字唯一,等价于@WebServlet的name属性
<servlet-class>String指定Servlet类的全限定名,即:包名.类名
<init-param>指定Servlet初始化参数,等价于@WebServlet的initParams属性,若有多个参数可重复定义此元素。此元素为可选配置
<param-name>String指定初始参数名
<param-value>String指定初始参数名对应的值
<load-on-startup>int指定Servlet的加载顺序,等价于@WebServlet的loadOnStartup属性
<async-supported>boolean指定Servlet是否支持异步操作模式,默认为false,等价于@WebServlet的asyncSupported属性

web.xml中<servlet-mapping> </servlet-mapping>元素用于指定Servlet的URL映射,其子元素及其描述如下表: servlet-mapping 可以有多个

属性名类型描述
<servlet-name>String用来指定要映射的Servlet名称,要与<servlet>声明中的<servlet-name>值一致
<url-pattern>String指定Servlet的URL匹配模式,等价于@WebServlet的urlPatterns属性或value属性

3.4.3 Servlet2.5 API配置 完整实例

3.4.3.1 路径映射

java
package com.neuedu.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 项目:      javaweb1206
 * 类名:       PathPatternsServlet
 * 创建时间:  2020/12/7  8:50
 * 描述 :     路径的配置
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class PathPatternsServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println("被请求了:"+req.getRequestURL().toString());
    }
}





package com.neuedu.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 项目:      javaweb1206
 * 类名:       InitParameterServlet
 * 创建时间:  2020/12/7  9:24
 * 描述 :     初始化携带参数的
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class InitParameterServlet extends HttpServlet {



    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       this.doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String url = super.getInitParameter("url");
        String username = super.getInitParameter("username");
        String password = super.getInitParameter("password");

        System.out.println("InitParameterServlet.doPost");
        System.out.println("url:"+url);
        System.out.println("username:"+username);
        System.out.println("password:"+password);
    }
}

3.4.3.2 web.xml

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>

  
  <servlet>
    <servlet-name>PathPatternsServlet</servlet-name>
    <servlet-class>com.neuedu.servlet.PathPatternsServlet</servlet-class>
  </servlet>
  
  <servlet>
    <servlet-name>FirstServlet</servlet-name>
    <servlet-class>com.neuedu.servlet.FirstServlet</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>InitParameterServlet</servlet-name>
    <servlet-class>com.neuedu.servlet.InitParameterServlet</servlet-class>
    <init-param>
      <param-name>url</param-name>
      <param-value>jdbc:mysql://127.0.0.1:3306/数据库2222222?userUnicode=true</param-value>
    </init-param>
    <init-param>
      <param-name>username</param-name>
      <param-value>admin</param-value>
    </init-param>
    <init-param>
      <param-name>password</param-name>
      <param-value>123456</param-value>
    </init-param>
  </servlet>



  <servlet-mapping>
    <servlet-name>FirstServlet</servlet-name>
    <!--http://127.0.0.1:8080/web/first-->
    <url-pattern>/first</url-pattern>
  </servlet-mapping>



  <!--http://127.0.0.1:8080/web/pathPatterns-->
  <servlet-mapping>
    <servlet-name>PathPatternsServlet</servlet-name>
    <url-pattern>/pathPatterns</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>PathPatternsServlet</servlet-name>
    <url-pattern>/path/pathPatterns</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>PathPatternsServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>PathPatternsServlet</servlet-name>
    <url-pattern>/path2/*</url-pattern>
  </servlet-mapping>


  <servlet-mapping>
    <servlet-name>PathPatternsServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>InitParameterServlet</servlet-name>
    <!--http://127.0.0.1:8080/web/init-->
    <url-pattern>/init</url-pattern>
  </servlet-mapping>

</web-app>

3.4.4 注解形式配置Servlet完整实例

java
package com.neuedu.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 项目:      javaweb1206
 * 类名:       ${NAME}
 * 创建时间:  2020/12/7  10:49
 * 描述 :     ${dc}
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
@WebServlet(
        name = "AnnotationServlet",
        urlPatterns = {
                "/anno",
                "/annotation"
        },
        loadOnStartup = 5,
        initParams={
                @WebInitParam(name="abc1",value="2021年12月7日10:57:07"),
                @WebInitParam(name="abc2",value="2022年12月7日10:57:07"),
                @WebInitParam(name="abc3",value="2023年12月7日10:57:07"),

        }
)
public class AnnotationServlet extends HttpServlet {


    public AnnotationServlet() {
        System.out.println("AnnotationServlet.AnnotationServlet");
    }



    @Override
    public void init() throws ServletException {
        super.init();
        String abc1 = getInitParameter("abc1");
        String abc2 = getInitParameter("abc2");
        String abc3 = getInitParameter("abc3");
        System.out.println("abc1:"+abc1);
        System.out.println("abc2:"+abc2);
        System.out.println("abc3:"+abc3);
        System.out.println("AnnotationServlet.init");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        PrintWriter out = response.getWriter();
        out.print("welcome AnnotationServlet");
        out.flush();
        out.close();
    }
}

3.5 Servlet生命周期

Servlet生命周期是指Servlet实例从创建到响应客户请求,直至销毁的过程。Servlet程序本身不直接在Java虚拟机上运行,由Servlet容器负责管理其整个生命周期。

Servlet生命周期可分为四个阶段:实例化、初始化、处理请求、销毁。

Servlet容器在如下时刻加载和实例化一个Servlet:

  • ​ 在Servlet容器启动后,客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet 对象的service方法。

  • 在为Servlet配置了自动装入选项(load-on-startup)时,服务器在启动时会自动装入此Servlet。

    Servlet初始化

    • Servlet实例化后,Servlet容器将调用Servlet的init方法来对Servlet实例进行初始化,初始化成功,Servlet在Web容器中会处于服务可用状态;如果初始化失败,Servlet容器会销毁该实例;
    • 当Servlet运行出现异常时,Servlet容器会使该实例变为服务不可用状态。
  1. 请求处理
    • 服务器接收到客户端请求,会为该请求创建“请求”对象和“响应”对象,并调用service()方法,service()方法再调用其他方法来处理请求.
    • 在Servlet生命周期中,service()方法可能被多次调用。当多个客户端同时访问某个Servlet的service()方法时,服务器会为每个请求创建一个线程,这样可以并行处理多个请求,减少请求处理的等待时间,提高服务器的响应速度。但同时也要注意对同一对象的并发访问问题。
  2. 服务终止
    • 当Servlet容器需要终止Servlet(如Web服务器被关闭或需要出让资源),它会先调用Servlet的destroy()方法使其释放正在使用的资源。
    • 在调用destroy()方法之前,必须让当前正在执行service()方法的任何线程完成执行,或者超过了服务器定义的时间限制。
    • 在destroy()方法完成后,Servlet容器必须释放Servlet实例以便被垃圾回收。
java
package com.neuedu.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 项目:      javaweb1206
 * 类名:       PathPatternsServlet
 * 创建时间:  2020/12/7  8:50
 * 描述 :     路径的配置
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class PathPatternsServlet extends HttpServlet {


    public PathPatternsServlet() {
        System.out.println("创建Servlet对象");
    }


    @Override
    public void init() throws ServletException {
        super.init();
        System.out.println("PathPatternsServlet.init");
    }

    /**
     * 尽量不重写service 方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
        System.out.println("处理请求 的 service");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this);
        System.out.println("被请求了:"+req.getRequestURL().toString());
    }

    @Override
    public void destroy() {
        super.destroy();
        System.out.println("PathPatternsServlet.destroy");
    }
}

3.6 Servlet的多线程并发问题

  • 由于servlet在Tomcat中是以单例模式存在的,所以当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。
    • 多线程下每个线程对局部变量都会有自己的一份copy,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,线程安全的。

解决办法:

​ 把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)

​ 建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。

Released under the MIT License.