JavaWeb - 09 Servlet相关内容

发布于:2022-12-17 ⋅ 阅读:(361) ⋅ 点赞:(0)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


1.Servlet 介绍

1.1 Servlet概述

  • Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

  • 狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

  • Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。我们可以像学习Java基础一 样,通过API来学习Servlet。这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用 JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,该网址中介绍了JavaEE8的一些新特性。当然,我们可 以通过访问官方API,学习和查阅里面的内容。

  • 来看一段参考手册中的概述

    Defines methods that all servlets must implement. 
    定义所有servlet必须实现的方法。
    
    A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol. 
    servlet是在Web服务器中运行的小型Java程序。servlet通常通过HTTP(超文本传输协议)接收和响应来自Web客户机的请求。
    
    To implement this interface, you can write a generic servlet that extends javax.servlet.GenericServlet or an HTTP servlet that extends javax.servlet.http.HttpServlet. 
    要实现这个接口,可以编写扩展javax.servlet.GenericServlet的通用servlet或扩展javax.servlet.http.HttpServlet的HTTP servlet。
    
    This interface defines methods to initialize a servlet, to service requests, and to remove a servlet from the server. These are known as life-cycle methods and are called in the following sequence: 
    	1.The servlet is constructed, then initialized with the init method. 
    	2.Any calls from clients to the service method are handled. 
    	3.The servlet is taken out of service, then destroyed with the destroy method, then garbage collected and finalized. 
    这个接口定义了初始化servlet、为请求提供服务以及从服务器删除servlet的方法。这些方法称为生命周期方法,按以下顺序调用:
    1.构建servlet,然后用init方法初始化。
    2.从客户端到服务方法的任何调用都将被处理。
    3.servlet从服务中取出,然后使用destroy方法销毁,然后垃圾收集并最终完成。
    
    In addition to the life-cycle methods, this interface provides the getServletConfig method, which the servlet can use to get any startup information, and the getServletInfo method, which allows the servlet to return basic information about itself, such as author, version, and copyright.
    除了生命周期方法之外,该接口还提供了getServletConfig方法和getServletInfo方法,servlet可以使用该方法获取任何启动信息,getServletInfo方法允许servlet返回关于自身的基本信息,例如作者、版本和版权。
    
  • 从上述英文介绍可以总结出:

    1. Servlet是运行在web服务端的java小程序
    2. 它可以接收响应客户端的请求
    3. 实现Servlet功能有三种方式:①实现Servlet类,重写所有方法。②继承GenericServlet,重写方法。③继承HttpServlet,重写方法。
    4. 每次请求都会执行service方法
    5. Servlet支持xml配置

1.2 Servlet入门案例-HttpServlet

开发步骤:

  1. 创建一个javaWeb项目,配置tomcat相关环境

  2. 导入Tomcat相关jar包servlet.jar、jsp.jar

    在这里插入图片描述

  3. 创建一个类DemoServlet01继承HttpServlet类

  4. 重写doGet、doPost方法

  5. 找到WEB_INF下的web.xml配置文件,配置访问路径

  6. 启动服务器,在浏览中访问地址

代码演示

DemoServlet01.java

// 定义一个类,继承httpServlet
public class DemoServlet01 extends HttpServlet {
    // 重写doGet和doPost方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DemoServlet.......");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml

<!--配置访问路径-->
<servlet>
    <servlet-name>DemoServlet01</servlet-name>
    <servlet-class>com.servlet.DemoServlet01</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet01</servlet-name>
    <url-pattern>/demo01</url-pattern>
</servlet-mapping>

1.3 在浏览其中的执行流程

在这里插入图片描述

1.4 Servlet解析过程

我们通过浏览器发送请求,请求首先到达Tomcat服务器,由服务器解析请求URL,然后在部署的应用列表中找到 我们的应用。接下来,在我们的应用中找应用里的web.xml配置文件,在web.xml中找到FirstServlet的配置,找到 后执行service方法,最后由FirstServlet响应客户浏览器。整个过程如下图所示:

在这里插入图片描述

一句话总结执行过程: 浏览器——>Tomcat服务器——>我们的应用——>应用中的web.xml——>FirstServlet——>响应浏览器

1.5 Servlet类的视图关系

在《Tomcat和Http协议》这天课程和刚才的入门案例中,我们都定义了自己的Servlet,实现的方式都是选择继承 GenericServlet,在Servlet的API介绍中,它提出了我们除了继承GenericServlet外还可以继承HttpServlet,通过 查阅servlet的类视图,我们看到GenericServlet还有一个子类HttpServlet。同时,在service方法中还有参数 ServletRequest和ServletResponse,它们的关系如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NESg3Dn9-1663226661076)(4.png)]

2.Servlet的三种实现方式

2.1 实现Servlet接口的方式

开发步骤:

  1. 定义一个类DemoServlet02,实现Servlet接口。
  2. 重写Servlet中的所有抽象方法
  3. 配置web.xml映射关系

代码实现

DemoServlet02.java

public class DemoServlet02 implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("DemoServlet02....");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

web.xml

<servlet>
    <servlet-name>DemoServlet02</servlet-name>
    <servlet-class>com.servlet.DemoServlet02</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet02</servlet-name>
    <url-pattern>/demo02</url-pattern>
</servlet-mapping>

2.2 继承GenericServlet的方式

开发步骤

  1. 定义一个类DemoServlet03,继承GenericServlet抽象类。
  2. 重写GenericServlet中的所有抽象方法
  3. 配置web.xml映射关系

代码演示

DemoServlet03.java

public class DemoServlet03 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("DemoServlet03....");
    }
}

web.xml

<servlet>
    <servlet-name>DemoServlet03</servlet-name>
    <servlet-class>com.servlet.DemoServlet03</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet03</servlet-name>
    <url-pattern>/demo03</url-pattern>
</servlet-mapping>

2.3 继承HttpServlet的方式

该方式就是入门案例,就不再赘述。

3.Servlet的生命周期及线程安全

3.1 Servlet生命周期

Servlet的生命周期通俗的来讲就是对象从出生到死亡的过程:出生-活着-死亡。利用官方的话语就是从初始化到销毁的过程:初始化-运行-销毁。

首先我们来定义一个类DemoServlet04重写init、destroy、service方法,通过运行程序来看一下Servlet生命周期的运行时机

public class DemoServlet04 extends GenericServlet {
    // 初始化时期
    @Override
    public void init() throws ServletException {
        System.out.println("init...运行了...");
    }

    // 运行时期
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service....运行了....");
    }

    // 销毁时期
    @Override
    public void destroy() {
        System.out.println("destroy....运行了.......");
    }
}

在web.xml中配置访问路径

<servlet>
    <servlet-name>DemoServlet04</servlet-name>
    <servlet-class>com.servlet.DemoServlet04</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet04</servlet-name>
    <url-pattern>/demo04</url-pattern>
</servlet-mapping>

首先我们启动服务器,发现只是服务器启动了,控制台并没有什么变化,没有任何输出。说明服务器启动不会初始化任何Servlet对象。当我们通过浏览器向服务器发送第一次请求时,发现idea控制台输出了 init...运行了... service....运行了....两句话,这就说明了Servlet初始化的操作是在浏览器发送第一次请求时初始化的。之后每一次请求控制台只输出service...运行了...,从这个操作我们可以知道,Servlet智慧初始化操作一次,然后就会一直存在内存当中,之后每一次的请求只会调用service方法。当我们关闭服务器时,控制台就会输出destroy....运行了.......,这就说明当服务器关闭,Servlet对象会自动销毁。

总结分析:

  • 创建初始化(出生)时期:当浏览器向第一次请求到达Servlet时,Servlet对象创建并进行初始化。且只初始化一次,之后一直存在内存当中。
  • 运行(活着)时期:服务器运行过程中,该Servlet对象一直存在,并且每一次请求只会调用service方法。
  • 销毁(死亡)时期:当服务器关闭或宕机时,Servlet对象就自动销毁。

通过分析生命周期我们发现,实例初始化操作实在请求第一次到达Servlet时进行的,当服务器宕机或关闭时Servlet对象销毁。而在这个过程中Servlet对象只创建了一次,销毁了一次。所以在整个服务器运行的过程中,Servlet只有一个实例。如果一个对象的实例在整个应用中是唯一存在的,我们就称它为单例的,也就是运用了单例设计模式。

3.2 改变Servlet初始化时机

  • 默认情况下,Servlet对象初始化是第一次请求到达Servlet时进行的。服务器的启动Servlet并不会随之初始化。
  • 但是,如果想要Servlet随之服务启动进行初始化操作,这就要改变Servlet初始化的加载时机。
  • 设置加载时机的方式:在web.xml中添加load-on-startup参数进行设置。取值值范围1-10,数值越大优先级越低。

代码演示

<servlet>
    <servlet-name>DemoServlet04</servlet-name>
    <servlet-class>com.servlet.DemoServlet04</servlet-class>
    <!--设置初始化时机-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet04</servlet-name>
    <url-pattern>/demo04</url-pattern>
</servlet-mapping>

第一次请求创建初始化实例与启动时创建初始化实例的区别

  • 请求第一次访问是创建Servlet,它的优势就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。而它的弊端就是,如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。
  • 应用加载时创建Servlet,它的优势是在服务器启动时,就把需要的对象都创建完成了,从而在使用 的时候减少了创建对象的时间,提高了首次执行的效率。它的弊端也同样明显,因为在应用加载时就创建了 Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。

3.3 Servlet多线程安全问题

由于Servlet时单例模式,既整个程序运行过程中只有一个该实例对象。所以我们就有必要来分析这个唯一的对象是否会存在这线程安全的问题。首先我们先来看看如下代码:

public class DemoServlet05 extends HttpServlet {
    // 定义一个变量
    private String username = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 接收来自浏览器传过来的数据
        username = req.getParameter("username");

        // 使线程睡眠5s,增加暴露问题的概率
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 将数据返回给浏览器的页面
        resp.getWriter().print(username);
    }

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

web.xml

<servlet>
    <servlet-name>DemoServlet05</servlet-name>
    <servlet-class>com.servlet.DemoServlet05</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet05</servlet-name>
    <url-pattern>/demo05</url-pattern>
</servlet-mapping>

同时启用两个浏览器,分别向服务器中传入不同的值,如下结果:

在这里插入图片描述

这是我们就会发现,返回的结果是相同的。而我们期望的结果时地址栏传入什么,浏览器页面中就输出什么。显然预期和实际情况不符。这就出现了线程安全问题。其产生的原因是,每一个浏览器端都可以当做一个线程,那么多个浏览器就会产生多个线程。当多个线程同时访问一个共享的数据源时,其中的一个线程修改了数据源,必然会影响到其它线程,所以就会出现线程安全问题。

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

解决该问题的方式:

  1. 在Servlet中定义成员需谨慎。如果类成员是共用的,并且只会在初始化时赋值, 其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线 程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。
  2. 可以上锁,将操作共享数据的代码块进行加锁方式变成同步代码块。(不推荐,效率太低)
// 方式一:将其改成局部变量
public class DemoServlet05 extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = nulll;
        // 接收来自浏览器传过来的数据
        username = req.getParameter("username");

        // 使线程睡眠5s,增加暴露问题的概率
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 将数据返回给浏览器的页面
        resp.getWriter().print(username);
    }

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

// 方式二:加锁机制
public class DemoServlet05 extends HttpServlet {
    // 定义一个全局共享的变量
    private String username = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        synchronized (this){
            // 接收来自浏览器传过来的数据
            username = req.getParameter("username");

            // 使线程睡眠5s,增加暴露问题的概率
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 将数据返回给浏览器的页面
            resp.getWriter().print(username);
        }
    }

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

4.Servlet映射方式

4.1 单路映射的三种方式

首先编写一个Servlet类名为DemoServlet06

public class DemoServlet06 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DemoServlet06........");
    }

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

方式一:完全匹配

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

例如:/demo06

访问方式:http://localhost:8080/day11/demo06

<servlet>
    <servlet-name>DemoServlet06</servlet-name>
    <servlet-class>com.servlet.DemoServlet06</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet06</servlet-name>
    <url-pattern>/demo06</url-pattern>
</servlet-mapping>

方式二 : /开头+通配符的方式

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

例如:/demo06/*

访问方式: http://localhost:8080/day11/demo06/text

http://localhost:8080/day11/demo06/xxxx

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

<servlet>
    <servlet-name>DemoServlet06</servlet-name>
    <servlet-class>com.servlet.DemoServlet06</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet06</servlet-name>
    <url-pattern>/demo06/*</url-pattern>
</servlet-mapping>

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

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

例如:*.do

访问方式:http://localhost:8080/day11/aaa.do

<servlet>
    <servlet-name>DemoServlet06</servlet-name>
    <servlet-class>com.servlet.DemoServlet06</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet06</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

4.2 多路径映射

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

<servlet>
    <servlet-name>DemoServlet06</servlet-name>
    <servlet-class>com.servlet.DemoServlet06</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet06</servlet-name>
    <url-pattern>/demo06</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>DemoServlet06</servlet-name>
    <url-pattern>/mydemo06</url-pattern>
</servlet-mapping>

4.3 缺省Servlet

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

在这里插入图片描述

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

注意事项 :

  1. /:处理静态资源 , 处理404 , 不能处理jsp
  2. /* : 处理静态资源 , 处理404 , 能处理jsp

5.ServletConfig 对象

5.1 ServletConfig概述

  • 文档描述:A servlet configuration object used by a servlet container to pass information to a servlet during initialization.

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

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

5.2 ServletConfig配置初始化参数

它需要在servlet 标签中的<init-param>标签来配置

创建一个类DemoServlet07

public class DemoServlet07 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DemoServlet07........");
    }

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

在web.xml配置初始化参数

<servlet>
    <servlet-name>DemoServlet07</servlet-name>
    <servlet-class>com.servlet.DemoServlet07</servlet-class>
    <!--配置初始化参数-->
    <init-param>
        <!--用于获取初始化参数的key-->
        <param-name>encoding</param-name>
        <!--初始化参数的值-->
        <param-value>UTF‐8</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>DemoServlet07</servlet-name>
    <url-pattern>/demo07</url-pattern>
</servlet-mapping>

5.3 相关的方法

方法 描述
getInitParameter(String name) 根据初始化参数名称获取参数值
getInitParameterNames() 获取所有的初始化参数名称
getServletContext() 获取ServletContext对象
getServletName() 获取Servlet名称

代码演示 :

public class DemoServlet07 extends HttpServlet {
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 获取所有的初始化参数名称
        Enumeration<String> initParameterNames = config.getInitParameterNames();

        // 根据参数名称获取参数值
        while (initParameterNames.hasMoreElements()) {
            String name = initParameterNames.nextElement();
            String value = config.getInitParameter(name);
            System.out.println(name+"----"+value);
        }
    }
}

6.ServletContext对象

6.1 ServletContext 概述

首先看官方文档解释:

public interface ServletContextDefines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file. 
servletcontext定义一组servlet用来与其servlet容器通信的方法,例如,获取文件的MIME类型、发送请求或写入日志文件。

There is one context per "web application" per Java Virtual Machine. (A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace such as /catalog and possibly installed via a .war file.) 
每个Java虚拟机的每个“web应用程序”都有一个上下文。(“web应用程序”是一个servlet和内容的集合,安装在服务器URL命名空间的特定子集下,如/catalog,可能通过.war文件安装。)

In the case of a web application marked "distributed" in its deployment descriptor, there will be one context instance for each virtual machine. In this situation, the context cannot be used as a location to share global information (because the information won't be truly global). Use an external resource like a database instead. 
在web应用程序的部署描述符中标记为“分布式”的情况下,每个虚拟机都有一个上下文实例。在这种情况下,上下文不能用作共享全局信息的位置(因为信息不是真正的全局的)。使用外部资源,如数据库。

The ServletContext object is contained within the ServletConfig object, which the Web server provides the servlet when the servlet is initialized.
ServletContext对象包含在ServletConfig对象中,当servlet初始化时,Web服务器将向该对象提供servlet。

总结 :

  1. ServletContext对象,是一个应用上下文对象。
  2. 每一个应用只有一个ServletContext对象。
  3. 它可以让当前应用中每一个Servlet之间实现数据共享。

6.2 ServletContext生命周期

该对象的生命周期包含实力与初始化(出生)、运行(或者)、销毁(灭亡)三个阶段。

  • 出生(实例并初始化):在服务器启动时就实例并初始化,且一个应用只能有一个该对象实例。(与Servlet一样是单例设计模式)。
  • 活着(运行):只要应用一直提供服务,则该对象就一直存在。
  • 灭亡(销毁):服务应用关闭或是宕机,该对象自动销毁。

6.3 域对象概述

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

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

  • 在Servlet规范中,一共有4个域对象。今天我们讲解的ServletContext就是其中一个。它也是我们接触的第一个域 对象。它是web应用中最大的作用域,叫application域。每个应用只有一个application域。它可以实现整个应用间 的数据共享功能。

  • ServletContext域对象

    ServletContext是一个应用上下文对象,作用就是让一个服务应用中的servlet之间实现数据共享
    

    在这里插入图片描述

6.4 ServletContext配置

ServletContext既然被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配 置。它的配置被称为应用的初始化参数配置。 配置的方式,需要在 标签中使用 来配置初始化参数。具体代码如下:

<!--配置应用初始化参数-->
<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>

6.5 ServletContext常用的方法

方法 说明
getAttribute 获取域中的数据
setAttribute 设置域中的数据
removeAttribute 移除域中的数据
getContextPath 获取项目访问路径
getInitParameter 获取全局初始化参数
getInitParameterNames 获取所有的全局初始化参数名称
getRealPath 获取资源的真实路径/部署路径
getRequestDispatcher 获取请求调度器用于执行转发/包含
getMimeType 获取文件的mimeType

代码示例1

/*
 * 除了设置获取共享内容外的方法
 */
public class DemoServlet08 extends HttpServlet {
    private ServletConfig  config = null;

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取域对象
        ServletContext servletContext = config.getServletContext();

        // getContextPath获取项目访问路径
        String contextPath = servletContext.getContextPath();
        System.out.println("contextPath = " + contextPath);

        // getInitParameter获取全局初始化参数
        String encoding = servletContext.getInitParameter("encoding");
        System.out.println("encoding = " + encoding);

        // getInitParameterNames获取所有的全局初始化参数名称
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        System.out.println("initParameterNames = " + initParameterNames);

        // getRealPath获取资源的真实路径/部署路径
        String realPath = servletContext.getRealPath("index.jsp");
        System.out.println("realPath = " + realPath);

    }

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

代码实现2

/**
 * getAttribute获取域中的数据
 *  setAttribute设置域中的数据
 *  removeAttribute移除域中的数据
 */
public class DemoServlet09 extends HttpServlet {
    private ServletConfig config = null;

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取域对象
        ServletContext servletContext = config.getServletContext();

        // 设置共享内容
        servletContext.setAttribute("username","张三丰");
    }

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

public class DemoServlet10 extends HttpServlet {
    private ServletConfig config = null;

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取域对象
        ServletContext servletContext = config.getServletContext();

        // 获取共享内容
        String username = (String)servletContext.getAttribute("username");
        System.out.println("username = " + username);
    }

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

7.注解开发

7.1 Servlet3.0规范

  • 首先,我们要先跟同学们明确一件事情,我们在《Tomcat和HTTP协议》课程中已经介绍了,我们使用的是 Tomcat9,JavaEE规范要求是8,对应的Servlet规范规范应该是JavaEE8包含的4.x版本。 但是,同学们要知道,在企业级应用的开发中,稳定远比追新版本重要的多。所以,我们虽然用到了Tomcat9和对 应的JavaEE8,但是涉及的Servlet规范我们降板使用,用的是Servlet3.1版本。

  • 关于兼容性问题,同学们也无须担 心,向下兼容的特性,在这里也依然适用。 接下来,同学还有可能疑惑的地方就是,我们课程中明明使用的是Servlet3.1版本的规范,但是却总听老师提 Servlet3.0规范,这两个到底有怎样的联系呢?

  • 现在就给同学们解惑,在大概十多年前,那会还是Servlet2.5的版本的天下,它最明显的特征就是Servlet的配置要 求配在web.xml中,我们今天课程中在第4章节《注解开发Servlet》之前,全都是基于Servlet2.5规范编写的。

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

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

7.2 注解开发

语法:

@WebServlet , @WebFilter , @WebListener

开发步骤

  1. 定义一个类继承HttpServlet
  2. 在类上添加@WebServlet注解,并配置访问地址
  3. 启动服务测试

代码实现

@WebServlet("/demo11")
public class DemoServlet11 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DemoServlet11......");
    }

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

WebServlet注解源码

/**
* WebServlet注解
* @since Servlet 3.0 (Section 8.1.1)
*/
@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() default1;
    /**
	* 用于配置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 "";
}

7.3 手动创建容器

前置说明

在使用Servlet3.1版本的规范时,脱离了web.xml进行注解开发,它除了支持使用注解的配置方式外,还支持纯手 动创建Servlet容器的方式。要想使用的话,必须遵循它的编写规范。它是从Servlet3.0规范才开始引入的,加入了 一个新的接口:

package javax.servlet;
import java.util.Set;
/**
 * 初始化Servlet容器必须实现此接口
 * 它是Servlet3.0规范提供的标准接口
 * @since Servlet 3.0
 */
public interface ServletContainerInitializer {
    /**
	 * 启动容器时做一些初始化操作,例如注册Servlet,Filter,Listener等等。
	 * @since Servlet 3.0
	 */
    void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}

同时可以利用@HandlesTypes注解,把要加载到onStartup方法中的类字节码传入进来,@HandlesTypes源码如 下:

/**
* 用于指定要加载到ServletContainerInitializer接口实现了中的字节码
* @see javax.servlet.ServletContainerInitializer
* @since Servlet 3.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes {
    /**
	 * 指定要加载到ServletContainerInitializer实现类的onStartUp方法中类的字节码。
	 * 字节码可以是接口,抽象类或者普通类。
	 */
    Class[] value();
}

编写步骤

  1. 创建一个Servlet类DemoServlet12

    public class DemoServlet12 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("DemoServlet12......");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  2. 编写初始化容器并按要求配置配置文件

    public class MyServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
            
        }
    }
    

    在脱离web.xml时,要求在src目录下包含一个META-INF目录,位置和及字母都不能改变,且严格区分大小写。在 目录中创建一个名称为 javax.servlet.ServletContainerInitializer 的文件,里面写实现了 ServletContainerInitializer 接口的全限定类名。如下图所示:

    在这里插入图片描述

  3. 在容器中编写Servlet注册代码

    public class MyServletContainerInitializer implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
            // 1.创建Servler对象
            DemoServlet12 servlet = new DemoServlet12();
    
            // 2.在上下文中添加Servlet,并得到Servlet的配置对象
            ServletRegistration.Dynamic registration = servletContext.addServlet("DemoServlet12", servlet);
            // 3.注册访问映射
            registration.setLoadOnStartup(1);
            registration.addMapping("/demo12");
            registration.setAsyncSupported(false);
        }
    }
    
  4. 重启服务器测试


网站公告

今日签到

点亮在社区的每一天
去签到