Servlet

发布时间:2022-07-04 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Servlet脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

目录
  • Servlet 介绍
  • Servlet 基础使用
    • Servlet 编写步骤
    • Servlet 执行过程
    • Servlet 类视图
    • Servlet 编写方式
    • Servlet 生命周期
    • Servlet 线程安全
    • Servlet 映射配置
      • 方式一:精确映射
      • 方式二:/开头+通配符
      • 方式三:通配符+固定格式结尾
      • 优先级
    • 多路径映射 Servlet
    • 启动时即创建 Servlet
    • 默认 Servlet
    • Servlet 关系总图
  • Servletconfig
    • ServletConfig 简介
    • ServletConfig 使用
  • ServletContext
    • ServletContext概述
    • ServletContext 使用
      • 获取
      • 配置
  • Servlet 注解开发

Servlet 介绍

Servlet 是 SUN 公司提供的一套规范,名称就叫 Servlet 规范,它也是 JavaEE 规范之一。我们可以通过访问官方 API 学习和查阅里面的内容。

打开官方 API 网址,在左上部分找到 javax.servlet 包,在左下部分找到 Servlet,如下图显示:

Servlet

通过阅读 API,我们可以得到如下信息:

  1. Servlet 是一个运行在 Web 服务端的 Java 小程序。
  2. 它可以用于接收和响应客户端的请求。
  3. 要想实现 Servlet 功能,可以实现 Servlet 接口、继承 GenericServlet 或者 HttpServlet。
  4. 每次请求都会执行 service 方法。
  5. Servlet 还支持配置。

Servlet

Servlet 基础使用

Servlet 编写步骤

1)编码

  1. 前期准备-创建 Java Web 工程;

  2. 编写一个普通类继承 GenericServlet 并重写 service 方法;

  3. 在 web.XMl 配置 Servlet;

2)测试

  1. 在 Tomcat 中部署项目;

  2. 在浏览器访问 Servlet。

Servlet

Servlet 执行过程

过程如下图所示:

Servlet

  1. 我们通过浏览器发送请求,请求首先到达 Tomcat 服务器
  2. 由服务器解析请求 URL,然后在部署的应用列表中找到我们的应用。
  3. 在我们的应用中找到 web.xML 配置文件,在 web.xml 中找到 Servlet 的配置。
  4. 找到后执行 service 方法。
  5. 最后由 Servlet 响应客户浏览器。

Servlet 类视图

  • 在 Servlet 的 API 介绍中,除了继承 GenericServlet 外还可以继承 HttpServlet。
  • 通过查阅 servlet 的类视图,我们看到 GenericServlet 还有一个子类 HttpServlet。
  • 同时,在 service 方法中还有参数 ServletRequest 和 ServletResponse。

它们的关系如下图所示:

Servlet

Servlet 编写方式

我们在实现 Servlet 功能时,可以选择以下三种方式:

第一种:实现 Servlet 接口,接口中的方法必须全部实现。

  • 使用此种方式,表示接口中的所有方法在需求方面都有重写的必要。此种方式支持最大程度的自定义。

第二种:继承 GenericServlet,service 方法必须重写,其他方可根据需求,选择性重写。

  • 使用此种方式,表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使我们的开发 Servlet 变得简单。但是,此种方式是和 HTTP 协议无关的。

第三种:继承 HttpServlet

  • 它是 javax.servlet.http 包下的一个抽象类,是 GenericServlet 的子类。
  • 如果我们选择继承 HttpServlet 时,只需要重写 doGet 和 doPost 方法,不需要覆盖 service 方法。
  • 使用此种方式,表示我们的请求和响应需要和 HTTP 协议相关。也就是说,我们是通过 HTTP 协议来访问的。那么每次请求和响应都符合 HTTP 协议的规范。请求的方式就是 HTTP 协议所支持的方式(HTTP 协议支持 7 种请求方式:GET、POST、PUT、DELETE、TRACE、OPTIONS、HEAD)。
  • 为了实现代码的可重用性,通常我们只需要在 doGet 或者 doPost 方法任意一个中提供具体功能即可,而另外的那个方法只需要调用提供了功能的方法。

Servlet 生命周期

对象的生命周期,就是对象从生到死的过程,即:出生——活着——死亡。用更偏向于开发的官方说法,就是对象从被创建到销毁的过程

  1. 出生:请求第一次到达 Servlet 时,Servlet 对象就会被创建出来,并且初始化成功。只出生一次,放到内存中。

  2. 活着:服务器提供服务的整个过程中,该 Servlet 对象一直存在,每次只是执行 service 方法。

  3. 死亡:当服务停止时,或者服务器宕机时,Servlet 对象消亡。

通过分析 Servlet 的生命周期可以发现,它的实例化和初始化只会在请求第一次到达 Servlet 时执行,而销毁只会在 Tomcat 服务器停止时执行。

由此我们得出一个结论,Servlet 对象只会创建一次,销毁一次。所以,每一个 Servlet 只有一个实例对象。如果一个对象实例在应用中是唯一的存在,那么我们就说它是单实例的,即运用了单例模式。

Servlet 线程安全

由于 Servlet 运用了单例模式,即在整个应用中,每一个 Servlet 类只有一个实例对象,所以我们需要分析这个唯一的实例中的类成员是否线程安全。

接下来,我们来看下面的的示例:

public class ServletDemo extends HttpServlet {
    //1.定义用户名成员变量
    //PRivate String username = null;

    @override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        //synchronized (this) {
            //2.获取用户名
            username = req.getParameter("username");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //3.获取输出流对象
            PrintWrITer pw = resp.getWriter();

            //4.响应给客户端浏览器
            pw.print("welcome:" + username);

            //5.关流
            pw.close();
        //}
    }

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

启动两个浏览器,输入不同的参数,访问之后发现输出的结果都是一样,所以出现线程安全问题:

Servlet

通过上面的测试我们发现,在 Servlet 中定义了类成员后,多个浏览器都会共享类成员的数据。每一个浏览器端就代表是一个线程,那么多个浏览器就是多个线程,所以测试的结果说明了多个线程会共享 Servlet 类成员中的数据。那么,其中任何一个线程修改了数据,都会影响其他线程。因此,我们可以认为 Servlet 不是线程安全的。

分析产生这个问题的根本原因,其实就是因为 Servlet 是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。

要解决这个线程安全问题,需要在 Servlet 中定义类成员时慎重。

  • 如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题的。
  • 但如果类成员并非共用,或者每次使用都有可能对其赋值(如上图示例),那么就要考虑线程安全问题了,解决方案是把它定义到 doGet 或者 doPost 方法中。

Servlet 映射配置

Servlet 支持三种映射方式,以达到灵活配置的目的。

首先编写一个Servlet,代码如下:

public class ServletDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("ServletDemo5接收到了请求");
    }

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

方式一:精确映射

此种方式,只有和映射配置一模一样时,Servlet 才会接收和响应来自客户端的请求。

Servlet

方式二:/开头+通配符

此种方式,只要符合目录结构即可,不用考虑结尾是什么

例如:映射为:/servlet/*

  • 访问 http://localhost:8585/servlet/itheima 或 http://localhost:8585/servlet/itcast.do 这两个 URL 都可以。

  • 因为用的*,表示 /servlet/ 后面的内容是什么都可以。

Servlet

方式三:通配符+固定格式结尾

此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)

例如:映射为:*.do

  • 访问 URL:http://localhost:8585/servlet/itcast.do 或 http://localhost:8585/itheima.do 这两个 URL 都可以。
  • 因为都是以 .do 作为结尾,而前面用 * 号通配符配置的映射。

Servlet

优先级

通过测试我们发现,Servlet 支持多种配置方式,但是由此也引出了一个问题,当有两个及以上的 Servlet 映射都符合请求 URL 时,由谁来响应呢?

注意:HTTP 协议的特征是一请求一响应的规则。那么有一个请求,必然有且只有一个响应。所以,映射规则的优先级如下:

  1. 精确匹配
  2. /开头+通配符
  3. 通配符+固定格式结尾

Servlet

多路径映射 Servlet

这其实是给一个 Servlet 配置多个访问映射,从而可以根据不同请求 URL 实现不同的功能。

示例 Servlet:

public class ServletDemo extends HttpServlet {

    /**
     * 根据不同的请求URL,做不同的处理规则
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1. 获取当前请求的 URI
        String uri = req.getRequestURI();
        uri = uri.substring(uri.lastIndexOf("/"), uri.length());
        //2. 判断是1号请求还是2号请求
        if("/servletDemo7".equals(uri)){
            System.out.println("ServletDemo7执行1号请求的业务逻辑:商品单价7折显示");
        }else if("/demo7".equals(uri)){
            System.out.println("ServletDemo7执行2号请求的业务逻辑:商品单价8折显示");
        }else {
            System.out.println("ServletDemo7执行基本业务逻辑:商品单价原价显示");
        }
    }

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

web.xml 配置 Servlet:

<servlet>
    <servlet-name>servletDemo7</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo7</servlet-class>
</servlet>
<!--映射路径1-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/demo7</url-pattern>
</servlet-mapping>
<!--映射路径2-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/servletDemo7</url-pattern>
</servlet-mapping>
<!--映射路径3-->
<servlet-mapping>
    <servlet-name>servletDemo7</servlet-name>
    <url-pattern>/servlet/*</url-pattern>
</servlet-mapping>

启动服务,测试运行结果:

Servlet

启动时即创建 Servlet

Servlet 的创建默认情况下是请求第一次到达 Servlet 时创建的。但是我们知道,Servlet 是单例的,也就是说在应用中只有唯一的一个实例,所以在 Tomcat 启动加载应用的时候就创建也是一个很好的选择。那么两者有什么区别呢?

  • 第一种:应用加载时创建 Servlet

    • 它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。
    • 它的弊端也同样明显,因为在应用加载时就创建了 Servlet 对象,因此,有可能导致内存中充斥着大量用不上的 Servlet 对象,造成了内存的浪费。
  • 第二种:请求第一次访问是创建 Servlet

    • 它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的 Servlet 对象就不会被创建,同时也提高了服务器的启动时间。
    • 而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,那么它就没法完成,从而要考虑其他技实现。

通过上面的分析可得出,当需要在应用加载就要完成一些工作时,就需要选择第一种方式;当有很多 Servlet 且其使用时机并不确定时,就选择第二种方式

在 web.xml 中是支持对 Servlet 的创建时机进行配置的,配置的方式如下:

<servlet>
    <servlet-name>servletDemo3</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo3</servlet-class>
    <!-- 配置Servlet的创建顺序,当配置此标签时,Servlet就会改为应用加载时创建
        配置项的取值只能是正整数(包括0),数值越小,表明创建的优先级越高。
    -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo3</servlet-name>
    <url-pattern>/servletDemo3</url-pattern>
</servlet-mapping>

Servlet

默认 Servlet

默认 Servlet 是由 Web 服务器提供的一个 Servlet,它配置在 Tomcat 的 conf 目录下的 web.xml 中。如下图所示:

Servlet

它的映射路径是<url-pattern>/<url-pattern>。在我们发送请求时,首先会在我们应用中的 web.xml 中查找映射配置,找到就执行。当找不到对应的 Servlet 路径时,就会去找默认的 Servlet,由默认 Servlet 处理。所以,一切都是 Servlet。

Servlet 关系总图

Servlet

ServletConfig

ServletConfig 简介

概念

  • ServletConfig 是 Servlet 的配置参数对象。
  • 在 Servlet 规范中,允许为每个 Servlet 都提供一些初始化配置。所以,每个 Servlet 都一个自己的 ServletConfig。
  • 它的作用是在 Servlet 初始化期间,把一些配置信息传递给 Servlet。

生命周期

  • 由于 ServletConfig 是在初始化阶段读取了 web.xml 中为 Servlet 准备的初始化配置,并把配置信息传递给 Servlet,所以生命周期与 Servlet 相同。
  • 这里需要注意的是,如果 Servlet 配置了<load-on-startup>1</load-on-startup>,那么 ServletConfig 也会在应用加载时创建。

ServletConfig 使用

获取

ServletConfig 可以为每个 Servlet 都提供初始化参数,所以肯定可以在每个 Servlet 中都配置。

public class ServletDemo8 extends HttpServlet {

    // 定义 Servlet 配置对象 ServletConfig
    private ServletConfig servletConfig;

    /**
     * 在初始化时为 ServletConfig 赋值
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 输出ServletConfig
        System.out.println(servletConfig);
    }

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

web.xml:

<servlet>
    <servlet-name>servletDemo8</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo8</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo8</servlet-name>
    <url-pattern>/servletDemo8</url-pattern>
</servlet-mapping>

配置

上面我们已经准备好了 Servlet,同时也获取到了它的 ServletConfig 对象,而如何配置初始化参数,则需要使用<servlet>标签中的<init-param>标签来配置。

即 Servlet 的初始化参数都是配置在 Servlet 的声明部分的,并且每个 Servlet 都支持有多个初始化参数,并且初始化参数都是以键值对的形式存在的。

配置示例:

<servlet>
    <servlet-name>servletDemo8</servlet-name>
    <servlet-class>com.itheima.web.servlet.ServletDemo8</servlet-class>
    <!--配置初始化参数-->
    <init-param>
        <!--用于获取初始化参数的key-->
        <param-name>encoding</param-name>
        <!--初始化参数的值-->
        <param-value>UTF-8</param-value>
    </init-param>
    <!--每个初始化参数都需要用到init-param标签-->
    <init-param>
        <param-name>servletInfo</param-name>
        <param-value>This is Demo8</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>servletDemo8</servlet-name>
    <url-pattern>/servletDemo8</url-pattern>
</servlet-mapping>

常用方法

Servlet

示例:

/**
 * 演示Servlet的初始化参数对象
 * @author 黑马程序员
 * @Company http://www.itheima.COM
 */
public class ServletDemo8 extends HttpServlet {

    // 定义 Servlet 配置对象 ServletConfig
    private ServletConfig servletConfig;

    /**
     * 在初始化时为 ServletConfig 赋值
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 输出ServletConfig
        System.out.println(servletConfig);
        // 2. 获取Servlet的名称
        String servletName= servletConfig.getServletName();
        System.out.println(servletName);
        // 3. 获取字符集编码
        String encoding = servletConfig.getInitParameter("encoding");
        System.out.println(encoding);
        // 4. 获取所有初始化参数名称的枚举
        Enumeration<String> names = servletConfig.getInitParameterNames();
        //遍历names
        while(names.hasMoreElements()){
            //取出每个name(key)
            String name = names.nextElement();
            //根据key获取value
            String value = servletConfig.getInitParameter(name);
            System.out.println("name:"+name+",value:"+value);
        }
        // 5. 获取ServletContext对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
    }

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

Servlet

ServletContext

ServletContext概述

ServletContext 对象是应用上下文对象

每一个应用有且只有一个 ServletContext 对象,它可以实现让应用中所有 Servlet 间的数据共享。

生命周期

  1. 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象(Servlet 和 ServletContext 都是单例的)。

  2. 活着:只要应用一直提供服务,该对象就一直存在。

  3. 死亡:应用停止(或者服务器挂了),该对象消亡。

域对象概念

  • 域对象指的是对象有作用域,即有作用范围

  • 域对象的作用,域对象可以实现数据共享。不同作用范围的域对象,共享数据的能力不一样。

  • 在 Servlet 规范中,一共有 4 个域对象,ServletContext 就是其中一个。

  • ServletContext 是 web 应用中最大的作用域,叫application 域。每个应用只有一个 application 域,它可以实现整个应用间的数据共享功能。

ServletContext 使用

获取

只需要调用 ServletConfig 对象的getServletContext()方法就可以了:

public class ServletDemo9 extends HttpServlet {

    // 定义 Servlet 配置对象 ServletConfig
    private ServletConfig servletConfig;

    /**
     * 在初始化时为 ServletConfig 赋值
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取 ServletContext 对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
    }

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

web.xml:

<servlet>
	<servlet-name>servletDemo9</servlet-name>
	<servlet-class>com.itheima.web.servlet.ServletDemo9</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>servletDemo9</servlet-name>
	<url-pattern>/servletDemo9</url-pattern>
</servlet-mapping>

更简洁的获取方法

在实际开发中,如果每个 Servlet 对 ServletContext 都使用频繁的话,那么每个 Servlet 里定义 ServletConfig,再获取 ServletContext 的代码将非常多,造成大量的重复代码。

而 Servlet 规范的定义中也为我们想到了这一点,所以它在 GenericServlet 中,已经为我们声明好了 ServletContext 获取的方法,如下图所示:

Servlet

示例 Servlet 都是继承自 HttpServlet,而 HttpServlet 又是 GenericServlet 的子类,所以我们在获取 ServletContext 时,如果当前 Servlet 没有用到它自己的初始化参数时,就可以不用再定义初始化参数了,而是直接改成下图所示的代码即可:

Servlet

配置

ServletContext 既然被称之为应用上下文对象,那么它的配置就是针对整个应用的配置,而非某个特定 Servlet 的配置。它的配置被称为应用的初始化参数配置。

配置的方式,需要在<web-app>标签中使用<context-param>来配置初始化参数。具体代码如下:

<!--配置应用初始化参数-->
<context-param>
    <!--用于获取初始化参数的 key-->
    <param-name>servletContextInfo</param-name>
    <!--初始化参数的值-->
    <param-value>This is application scoPE</param-value>
</context-param>
<!--每个应用初始化参数都需要用到 context-param 标签-->
<context-param>
    <param-name>globalEncoding</param-name>
    <param-value>UTF-8</param-value>
</context-param>

Servlet 注解开发

Servlet 3.0 规范

在大概十多年前,那会还是 Servlet 2.5 的版本的天下,它最明显的特征就是 Servlet 的配置要求配在 web.xml 中。

从 2007 年开始到 2009 年底的这个时间段中,软件开发开始逐步的演变,基于注解的配置理念开始逐渐出现,大量注解配置思想开始用于各种框架的设计中,例如:Spring 3.0 版本的 Java Based Configuration、JPA 规范、apache 旗下的 struts2 和 mybatis 的注解配置开发等等

JavaEE6 规范也是在这个期间设计并推出的,与之对应就是它里面包含了新的 Servlet 规范:Servlet 3.0 版本

使用实例

配置步骤

步骤一:创建 Java Web 工程,并移除 web.xml

Servlet

Servlet

步骤二:编写 Servlet

public class ServletDemo1 extends HttpServlet {

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet Demo1 Annotation");
    }
}

步骤三:使用注解配置 Servlet

Servlet

步骤四:测试

Servlet

注解码分析

/**
 * WebServlet注解
 * @since Servlet 3.0
 */
@Target(ElementType.TYPE)
@Retention(Retentionpolicy.RUNTIME)
@Documented
public @interface WebServlet {

    /**
     * 指定Servlet的名称。
     * 相当于xml配置中<servlet>标签下的<servlet-name>
     */
    String name() default "";

    /**
     * 用于映射Servlet访问的url映射
     * 相当于xml配置时的<url-pattern>
     */
    String[] value() default {};

    /**
     * 相当于xml配置时的<url-pattern>
     */
    String[] urlPatterns() default {};

    /**
     * 用于配置Servlet的启动时机
     * 相当于xml配置的<load-on-startup>
     */
    int loadOnStartup() default -1;

    /**
     * 用于配置Servlet的初始化参数
     * 相当于xml配置的<init-param>
     */
    WebInitParam[] initParams() default {};

    /**
     * 用于配置Servlet是否支持异步
     * 相当于xml配置的<async-supported>
     */
    boolean asyncSupported() default false;

    /**
     * 用于指定Servlet的小图标
     */
    String smallIcon() default "";

    /**
     * 用于指定Servlet的大图标
     */
    String largeIcon() default "";

    /**
     * 用于指定Servlet的描述信息
     */
    String description() default "";

    /**
     * 用于指定Servlet的显示名称
     */
    String displayName() default "";
}

脚本宝典总结

以上是脚本宝典为你收集整理的Servlet全部内容,希望文章能够帮你解决Servlet所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。