【Java学习路线之JavaWeb】Servlet教程_javaweb servlet项目教程

本 Servlet 教程适合初学者阅读,可以帮助读者了解 Servlet 的基本功能及使用。完成本教程后,您将具备使用 Servlet 开发 Web 应用程序的能力,并为后续学习 JSP 等技术打下坚实的基础。

阅读条件

阅读本教程,您需要掌握 Java 编程基础,如果您对 Java 不了解,请转到《Java基础教程》。

Servlet到底是什么(非常透彻)

Servlet 是 Server Applet 的缩写,译为“服务器端小程序”,是一种使用 Java 语言来开发动态网站的技术。

Servlet 虽然被称作“小程序”,但是它的功能却异常强大,因为它是基于 Java 的,几乎可以使用所有的 Java API,Java 能做的事情,Servlet 也能做。

Java 是一种功能强大的通用型编程语言,可以处理 HTTP 请求,可以访问数据库,可以生成 HTML 代码,您完全可以使用原生 Java 来开发动态网站。但是,使用原生 Java 开发动态网站非常麻烦,需要自己解析 HTTP 请求的报头,需要自己分析用户的请求参数,需要自己加载数据库组件……种种原因导致使用原生 Java 开发动态网站几乎是一件不能被接受的事情。正是基于这种原因,Java 官方后来推出了 Servlet 技术,它对开发动态网站需要使用的原生 Java API 进行了封装,形成了一套新的 API,称为 Servlet API。

使用 Servlet 开发动态网站非常方便,程序员只需要集中精力处理业务逻辑,不需要再为那些基础性的、通用性的功能编写代码,这使得 Servlet 在动态网站开发领域具备了很高的实用性。

Servlet 基于 Java,可以使用几乎全部的 Java API,所以它的功能异常强大,完全可以胜任企业级开发,能够处理那些高并发、大吞吐量、业务逻辑复杂的应用场景。

您可以这样理解,Servlet 是 Sun 公司推出的一种基于 Java 的动态网站开发技术。编写 Servlet 代码需要遵循 Java 语法,一个 Servlet 程序其实就是一个按照 Servlet 规范编写的 Java 类。Servlet 程序需要先编译成字节码文件(.class文件),然后再部署到服务器运行。

Servlet 是一种规范

严格来说,Servlet 只是一套 Java Web 开发的规范,或者说是一套 Java Web 开发的技术标准。只有规范并不能做任何事情,必须要有人去实现它。所谓实现 Servlet 规范,就是真正编写代码去实现 Servlet 规范提到的各种功能,包括类、方法、属性等。

Servlet 规范是开放的,除了 Sun 公司,其它公司也可以实现 Servlet 规范,目前常见的实现了 Servlet 规范的产品包括 Tomcat、Weblogic、Jetty、Jboss、WebSphere 等,它们都被称为“Servlet 容器”。Servlet 容器用来管理程序员编写的 Servlet 类。

Servlet 接口

所有的 Servlet 功能都是通过一个名为Servlet的接口(Interface)向外暴露的,编写 Servlet 代码,可以从实现 Servlet 接口开始,就像下面这样:

public class ServletDemo implements Servlet {
    //TODO:
}

直接实现 Servlet 接口比较麻烦,需要实现很多方法,所以 Servlet 规范又提供了两个抽象类,分别是 GenericServlet 类和 HttpServlet 类,它们都实现了 Servlet 接口的很多常用功能。和 GenericServlet 类相比,HttpServlet 类更加方便,所以实际开发中一般都继承自 HttpServlet 类。

JSP

Servlet 是第一代 Java Web 开发技术,它将 HTML 代码以字符串的形式向外输出,编写 HTML 文档就是在拼接字符串,非常麻烦,所以 Java 官方又推出了第二代 Web 开发技术——JSP。

JSP 才是现代化的 Web 开发技术,它允许 HTML 代码和 JSP 代码分离,让程序员能够在 HTML 文档中直接嵌入 JSP 代码。

现在没有人直接使用 Servlet 开发动态网站,大家都转向了 JSP 阵营。但是 JSP 依赖于 Servlet,用户访问 JSP 页面时,JSP 代码会被翻译成 Servlet 代码,最终,HTML 代码还是以字符串的形式向外输出的。您看,JSP 只是在 Servlet 的基础上做了进一步封装。

JSP 代码可以调用 Servlet 类,程序员可以将部分功能在 Servlet 中实现,然后在 JSP 中调用即可。

总之,Servlet 是 JSP 的基础,Servlet 虽然不直接面向用户,但是它依然是 JSP 的后台支撑,想玩转 JSP,必须先玩转 Servlet。

学习顺序:

  • 学习 Servlet 的正确顺序是:Java --> Servlet。
  • 学习 JSP 的正确顺序是:Java --> Servlet --> JSP。

Servlet 版本

截止到 2020 年 10 月,最新的 Servlet 版本是 4.0。下表列出了各种主要 Servlet 版本的发布日期及其对应的 Java 版本。

Servlet 版本发布日期JAVA EE/JDK 版本
Servlet 4.02017年10月Java EE 8
Servlet 3.12013年5月Java EE 7
Servlet 3.02009年12月Java EE 6 / Java SE 6
Servlet 2.52005年10月Java EE 5 / Java SE 5
Servlet 2.42003年11月J2EE 1.4 / J2SE 1.3
Servlet 2.32001年8月J2EE 1.3 / J2SE 1.2
Servlet 2.21999年8月J2EE 1.2 / J2SE 1.2
Servlet 2.11998年11月未指定
Servlet 2.0JDK 1.1
Servlet 1.01997年6月

Applet

您可能还听说过 Applet,它和 Servlet 是相对的:

  • Java Servlet 是“服务器端小程序”,运行在服务器上,用来开发动态网站;
  • Java Applet 是“客户端小程序”,一般被嵌入到 HTML 页面,运行在支持 Java 的浏览器中。

Applet 和 Servlet 都是基于 Java 的一种技术,功能都非常强大,但是 Applet 开发步骤繁杂,而且只能在安装 Java 虚拟机(JVM)的计算机上运行,现在已经被 JavaScript 全面替代,几乎没有人再学习 Applet。

Servlet容器(Web容器)是什么

您可能已经知道,部署动态网站一般需要 Web 服务器的支持,例如:

  • 运行 PHP 网站一般选择 Apache 或者 Nginx;
  • 运行 ASP/ASP.NET 网站一般选择 IIS;
  • 运行 Python 网站一般选择内置的 WSGI 服务器模块——wsgiref。

Web 服务器是一种对外提供 Web 服务的软件,它可以接收浏览器的 HTTP 请求,并将处理结果返回给浏览器。

在部署 Servlet 网站时,同样需要一种类似的软件,例如 Tomcat、Jboss、Jetty、WebLogic 等,但是它们通常被称为“容器”,而不是“服务器”,这究竟是为什么呢?Servlet 容器和传统意义上的服务器有什么不同呢?

本节我们先讲解传统 Web 服务器的架构模式,再讲解 Servlet 容器的架构模式,然后将它们进行对比,加深读者的理解。

Web 服务器

初学者可能认为,只要有 Web 服务器,我们编写的网站代码就可以运行了,就可以访问数据库了,就可以注册登录并发布文章了,这其实是一种误解。

我们通常所说的 Web 服务器,比如 Apache、Nginx、IIS 等,它们的功能往往都比较单一,只能提供 http(s) 服务,让用户访问静态资源(HTML 文档、图片、CSS 文件、JavaScript 文件等),它们不能执行任何编程语言,也不能访问数据库,更不能让用户注册和登录。

也就是说,如果只有 Web 服务器,那您只能部署静态网站,不能部署动态网站。要想部署动态网站,必须要有编程语言运行环境(运行时,Runtime)的和数据库管理系统的支持。

运行环境(运行时)

开发网站使用的编程语言一般都是脚本语言(比如 PHP、ASP、Python),部署网站时都是将源代码直接扔到服务器上,然而源代码自己并不能运行,必须要有解释器的支持;当用户访问动态页面时,解释器负责分析、编译和执行源代码,然后得到处理结果。

解释器是执行脚本语言的核心部件,除此以外还有一些辅助性的部件,例如:

  • 垃圾回收器:负责及时释放不需要的内存,腾出资源供其它页面使用;
  • 标准库:任何编程语言都会附带标准库,它们提供了很多通用性的功能,极大地提高了开发效率,避免重复造轮子。

我们习惯将以上各种支持脚本语言运行的部件统称为运行环境,或者运行时(Runtime)。

数据库

Web 服务器不带数据库,编程语言也不带数据库,数据库是一款独立的软件;要想实现用户注册、发布文章、提交评论等功能,就必须安装一款数据库,比如 MySQL、Oracle、SQL Server 等。

总结

部署动态网站一般至少需要三个组件,分别是 Web 服务器、脚本语言运行时和数据库,例如,部署 PHP 网站一般选择「Apache + PHP 运行时 + MySQL」的组合。

img

Web 容器

我们知道,Servlet 是基于 Java 语言的,运行 Servlet 必然少不了 JRE 的支持,它负责解析和执行字节码文件(.class文件)。然而 JRE 只包含了 Java 虚拟机(JVM)、Java 核心类库和一些辅助性性文件,它并不支持 Servlet 规范。要想运行 Servlet 代码,还需要一种额外的部件,该部件必须支持 Servlet 规范,实现了 Servlet 接口和一些基础类,这种部件就是 Servlet 容器。

Servlet 容器就是 Servlet 代码的运行环境(运行时),它除了实现 Servlet 规范定义的各种接口和类,为 Servlet 的运行提供底层支持,还需要管理由用户编写的 Servlet 类,比如实例化类(创建对象)、调用方法、销毁类等。

Servlet 中的容器和生活中的容器是类似的概念:生活中容器用来装水、装粮食,Servlet 中的容器用来装类,装对象。

读者可能会提出疑问,我们自己编写的 Servlet 类为什么需要 Servlet 容器来管理呢?这是因为我们编写的 Servlet 类没有 main() 函数,不能独立运行,只能作为一个模块被载入到 Servlet 容器,然后由 Servlet 容器来实例化,并调用其中的方法。

一个动态页面对应一个 Servlet 类,开发一个动态页面就是编写一个 Servlet 类,当用户请求到达时,Servlet 容器会根据配置文件(web.xml)来决定调用哪个类。

下图演示了 Servlet 容器在整个 HTTP 请求流程中的位置:

HTTP请求流程

您看,Web 服务器是整个动态网站的“大门”,用户的 HTTP 请求首先到达 Web 服务器,Web 服务器判断该请求是静态资源还是动态资源:如果是静态资源就直接返回,此时相当于用户下载了一个服务器上的文件;如果是动态资源将无法处理,必须将该请求转发给 Servlet 容器。

Servlet 容器接收到请求以后,会根据配置文件(web.xml)找到对应的 Servlet 类,将它加载并实例化,然后调用其中的方法来处理用户请求;处理结束后,Servlet 容器将处理结果再转交给 Web 服务器,由 Web 服务器将处理结果进行封装,以 HTTP 响应的形式发送给最终的用户。

常用的 Web 容器有 Tomcat、Jboss、Jetty、WebLogic 等,其中 Tomcat 由 Java 官方提供,是初学者最常使用的。

为了简化部署流程,Web 容器往往也会自带 Web 服务器模块,提供基本的 HTTP 服务,所以您可以不用再安装 Apache、IIS、Nginx 等传统意义上的服务器,只需要安装一款 Web 容器,就能部署 Servlet 网站了。正是由于这个原因,有的教材将 Tomcat 称为 Web 容器,有的教材又将 Tomcat 称为 Web 服务器,两者的概念已经非常模糊了。

将 Web 容器当做服务器使用后,上面的流程图就变成了下面的样子:

HTTP 请求流程

注意,Servlet 容器自带的 Web 服务器模块虽然没有传统的 Web 服务器强大,但是也足以应付大部分开发场景,对初学者来说是足够的。当然,您也可以将传统的 Web 服务器和 Servlet 容器组合起来,两者分工协作,各司其职,共同完成 HTTP 请求。

总结

Servlet 容器就是 Servlet 程序的运行环境,它主要包含以下几个功能:

  • 实现 Servlet 规范定义的各种接口和类,为 Servlet 的运行提供底层支持;
  • 管理用户编写的 Servlet 类,以及实例化以后的对象;
  • 提供 HTTP 服务,相当于一个简化的服务器。

Tomcat下载和安装(图解)

Servlet 是基于 Java 的动态网站开发技术,它的所有类和组件都是基于 Java 实现的,要想使用 Servlet,就必须提前配置好 Java 运行环境。相信各位读者都已经掌握了如何搭建 Java 环境,此处就不再赘述了,不了解的读者请转到:

搭建好了 Java 环境,再安装一款 Web 容器就可以运行 Servlet 代码了。市场上有很多 Web 容器,著名的有 Tomcat、Jetty、JBoss、WebLogic 等,初学者最常使用的是 Tomcat。Tomcat 是 Java 官方推出一款免费开源的 Web 容器,它占用资源少,扩展性好,运行稳定,支持负载均衡,并且易于学习和使用,所以成为最受欢迎的 Tomcat 容器。

本文将为您讲解 Tomcat 的下载和安装。

Tomcat 下载和安装

进入 Tomcat 官网(http://tomcat.apache.org/),在首页左侧的导航栏中找到“Download”分类,可以看到在它下面有多个版本的 Tomcat,如图1所示。

选择 Tomcat 版本
图1:选择 Tomcat 版本

不同的 Tomcat 版本支持的 Java 版本也不同,读者可以根据自己的 JDK/JRE 版本来选择对应的 Tomcat 版本,如下图所示:

Tomcat 版本最新子版本Servlet 规范JSP 规范EL 规范WebSocket 规范认证(JASIC) 规范Java 版本 JDK/JRE 版本
10.0.x(内测)10.0.05.03.04.02.02.08 以及更高
9.0.x9.0.364.02.33.01.11.18 以及更高
8.5.x8.5.563.12.33.01.11.17 以及更高
8.0.x(已被取代)8.0.53(已被取代)3.12.33.01.1N/A7 以及更高
7.0.x7.0.1043.02.22.21.1N/A6 以及更高 (对于 WebSocket,支持 7 以及更高)
6.0.x(已废弃)6.0.53(已废弃)2.52.12.1N/AN/A5 以及更高
5.5.x(已废弃)5.5.36(已废弃)2.42.0N/AN/AN/A1.4 以及更高
4.1.x(已废弃)4.1.40(已废弃)2.31.2N/AN/AN/A1.3 以及更高
3.3.x(已废弃)3.3.2(已废弃)2.21.1N/AN/AN/A1.1 以及更高

我的本地电脑使用的是 JDK 8,对应的 Tomcat 版本是 10 和 9,但是考虑到 Tomcat 10 还在内测阶段,可能不太稳定,所以我选择了 Tomcat 9。

如图1所示,点击”Tomcat 9”链接即可跳转到 Tomcat 9 下载页面,向下滚动鼠标就能看到具体的下载链接,请读者根据自己的操作系统选择对应的版本,如图2所示。

Tomcat 9 下载页面
图2:Tomcat 9 下载页面

Windows 下的 Tomcat 有安装版和解压版,下面说一下它们的区别。

安装版 Tomcat

安装版 Tomcat 下载后得到一个 exe 安装包,和其它软件的安装流程没有什么区别。

安装版 Tomcat 在安装过程中会自动进行相关配置,比如找到 JDK 的位置、读取环境变量 JAVA_HOME 等。安装版 Tomcat 在启动后以服务的方式运行,并且带有 GUI 管理工具(图形界面化的管理工具),用户可以很方便地通过界面进行管理和操作,非常友好。

解压版 Tomcat

解压版 Tomcat 下载后得到一个 zip 压缩包,是免安装的,使用 WinRAR 等软件解压到任意目录后就可以直接使用。

解压版 Tomcat 还可以到处移动,您可以将它从 A 文件夹移动到 B 文件夹,甚至移动到 U 盘,是真正的绿色版。当您不再使用时,直接删除文件夹即可卸载解压版 Tomcat。

我的建议

我建议读者使用解压版 Tomcat,除了安装和卸载方便,解压版 Tomcat 还没有 GUI 界面,只能通过使用各种配置文件和命令来操作和管理,这利于初学者学习 Tomcat 的启动和运行流程。这也是大部分 Tomcat 教材的建议。

我的电脑是 64 位,下载 64 位 Windows 解压版后得到 apache-tomcat-9.0.39-windows-x64.zip,将其解压缩到计算机某磁盘(最好不要选择 C 盘)即可。

img
图3:解压 Tomcat

解压缩完成后,打开 Tomcat 所在目录,可以看到它包含了很多子文件夹,它们的作用如下表所示:

子目录说明
bin命令中心(启动命令,关闭命令……)
conf配置中心(端口号,内存大小……)
libTomcat 的库文件。Tomcat 运行时需要的 jar 包所在的目录。
logs存放日志文件。
temp存储临时产生的文件,即缓存。
webapps存放项目的文件,web 应用放置到此目录下浏览器可以直接访问。
work编译以后的 class 文件。

Tomcat目录结构

Tomcat 解压完成以后会看到如表1所示的很多目录,这些目录都有各自的用途,初学者有必要了解一下。

子目录说明
bin命令中心(启动命令,关闭命令……)
conf配置中心(端口号,内存大小……)
libTomcat 的库文件。Tomcat 运行时需要的 jar 包所在的目录。
logs存放日志文件。
temp存储临时产生的文件,即缓存。
webapps存放项目的文件,web 应用放置到此目录下浏览器可以直接访问。
work编译以后的 class 文件。

1. bin 目录

bin 目录用来存放 Tomcat 命令,主要分为两大类,一类是以.sh结尾的 Linux 命令,另一类是以.bat结尾的 Windows 命令。很多环境变量都在此处设置,例如 JDK 路径、Tomcat 路径等。

bin 目录包含的内容
图1:bin 目录包含的内容

下面是几个常用的 Tomcat 命令:

  • startup.sh/startup.bat:用来启动 Tomcat;
  • shutdown.sh/shutdown.bat:用来关闭 Tomcat;
  • catalina.bat/ catalina.bat:用来设置 Tomcat 的内存。

2. conf 目录

conf 目录主要是用来存放 Tomcat 的配置文件,如下图所示:

conf 目录包含的内容
图2:conf 目录包含的内容

下面是常用到的几个文件:

  • server.xml 用来设置域名、IP、端口号、默认加载的项目、请求编码等;
  • context.xml 用来配置数据源等;
  • tomcat-users.xml 用来配置和管理 Tomcat 的用户与权限;
  • web.xml 可以设置 Tomcat 支持的文件类型;
  • 在 Catalina 目录下可以设置默认加载的项目。

3. lib 目录

lib 目录主要用来存放 Tomcat 运行需要加载的 jar 包。

lib 目录包含的内容
图3:lib 目录包含的内容

4. logs 目录

logs 目录用来存放 Tomcat 在运行过程中产生的日志文件,清空该目录中的文件不会对 Tomcat 的运行带来影响。

在 Windows 系统中,控制台的输出日志在 catalina.xxxx-xx-xx.log 文件中;在 Linux 系统中,控制台的输出日志在 catalina.out 文件中。

5. temp 目录

temp 目录用来存放 Tomcat 在运行过程中产生的临时文件,清空该目录中的文件不会对 Tomcat 的运行带来影响。

temp 目录包含的内容
图4:temp 目录包含的内容

6. webapps 目录

webapps 目录用来存放应用程序(也就是通常所说的网站),当 Tomcat 启动时会去加载 webapps 目录下的应用程序,我们编写的 Servlet 程序就可以放在这里。Tomcat 允许以文件夹、war 包、jar 包的形式发布应用。

webapps 目录包含的内容
图5:webapps 目录包含的内容

7. work 目录

work 目录用来存放 Tomcat 在运行时的编译文件(也即 class 字节码文件),例如 JSP 编译后的文件。清空 work 目录,然后重启 Tomcat,可以达到清除缓存的作用。

Servlet三种创建方式

在 Servlet 中,一个动态网页对应一个 Servlet 类,我们可以通过 web.xml 配置文件将 URL 路径和 Servlet 类对应起来。访问一个动态网页的过程,实际上是将对应的 Servlet 类加载、实例化并调用相关方法的过程;网页上显示的内容,就是通过 Servlet 类中的某些方法向浏览器输出的 HTML 语句。

所以,使用 Servlet 创建动态网页的第一步,就是创建 Servlet 类。

Servlet 规范的最顶层是一个名为 javax.servlet.Servlet 的接口,所有的 Servlet 类都要直接或者间接地实现该接口。直接实现 Servlet 接口不太方便,所以 Servlet 又内置了两个 Servlet 接口的实现类(抽象类),分别为 GenericServlet 和 HttpServlet,因此,创建 Servlet 类有如下三种方式:

  1. 实现 javax.servlet.Servlet 接口,重写其全部方法。
  2. 继承 javax.servlet.GenericServlet 抽象类,重写 service() 方法。
  3. 继承 javax.servlet.http.HttpServlet 抽象类,重写 doGet() 或 doPost() 方法。

Servlet、GenericServlet 、HttpServlet 的关系

下图展示了 Servlet、GenericServlet 以及 HttpServlet 三者之间的关系,其中 MyServlet 是我们自定义的 Servlet 类。

Servlet 关系图

由上图可知:

  1. GenericServlet 是实现了 Servlet 接口的抽象类。
  2. HttpServlet 是 GenericServlet 的子类,具有 GenericServlet 的一切特性。
  3. Servlet 程序(MyServlet 类)是一个实现了 Servlet 接口的 Java 类。

Servlet 接口

javax.servlet.Servlet 是 Servlet API 的核心接口,所有的 Servlet 类都直接或间接地实现了这一接口。

Servlet 接口中定义了 5 个方法,下面我们对他们做简单的介绍。

返回值方法备注
voidinit(ServletConfig config)Servlet 实例化之后,由 Servlet 容器调用,用来初始化 Servlet 对象。该方法只能被调用一次。 参数 config 用来向 Servlet 传递配置信息。
voidservice(ServletRequest req,ServletResponse res)Servlet 容器调用该方法处理客户端请求。
voiddestroy()服务器关闭、重启或者 Servlet 对象被移除时,由 Servlet 容器调用,负责释放 Servlet 对象占用的资源。
ServletConfiggetServletConfig()该方法用来获取 ServletConfig 对象,该对象中包含了 Servlet 的初始化参数。
StringgetServletInfo()该方法用于获取 Servlet 的信息,例如作者、版本、版权等。

示例 1

通过实现 Servlet 接口创建 Servlet,示例代码如下。

package net.biancheng.www;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet implements Servlet {
    //Servlet 实例被创建后,调用 init() 方法进行初始化,该方法只能被调用一次
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    }
    //返回 ServletConfig 对象,该对象包含了 Servlet 的初始化参数
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }
    //每次请求,都会调用一次 service() 方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //设置字符集
        servletResponse.setContentType("text/html;charset=UTF-8");
        //使用PrintWriter.write()方法向前台页面输出内容
        PrintWriter writer = servletResponse.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
    //返回关于 Servlet 的信息,例如作者、版本、版权等
    @Override
    public String getServletInfo() {
        return null;
    }
    //Servelet 被销毁时调用
    @Override
    public void destroy() {
    }
}

GenericServlet 抽象类

javax.servlet.GenericServlet 实现了 Servlet 接口,并提供了除 service() 方法以外的其他四个方法的简单实现。通过继承 GenericServlet 类创建 Servlet ,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。

GenericServlet 类中还提供了以下方法,用来获取 Servlet 的配置信息。

返回值方法备注
StringgetInitParameter(String name)返回名字为 name 的初始化参数的值,初始化参数在 web.xml 中进行配置。如果参数不存在,则返回 null。
EnumerationgetInitParameterNames()返回 Servlet 所有初始化参数的名字的枚举集合,若 Servlet 没有初始化参数,返回一个空的枚举集合。
ServletContextgetServletContext()返回 Servlet 上下文对象的引用。
StringgetServletName()返回此 Servlet 实例的名称。

示例 2

通过继承 GenericServlet 抽象类创建 Servlet,示例代码如下。

package net.biancheng.www;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        //设置字符集
        servletResponse.setContentType("text/html;charset=UTF-8");
        //使用PrintWriter.write()方法向前台页面输出内容
        PrintWriter writer = servletResponse.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
}

HttpServlet 抽象类

javax.servlet.http.HttpServlet 继承了 GenericServlet 抽象类,用于开发基于 HTTP 协议的 Servlet 程序。由于 Servlet 主要用来处理 HTTP 的请求和响应,所以通常情况下,编写的 Servlet 类都继承自 HttpServlet。

在 HTTP/1.1 协议中共定义了 7 种请求方式,即 GET、POST、HEAD、PUT、DELETE、TRACE 和 OPTIONS。

HttpServlet 针对这 7 种请求方式分别定义了 7 种方法,即 doGet()、doPost()、doHead()、doPut()、doDelete()、doTrace() 和 doOptions()。

HttpServlet 重写了 service() 方法,该方法会先获取客户端的请求方式,然后根据请求方式调用对应 doXxx 方法。

示例 3

由于我们使用的请求方式主要是 GET 和 POST,所以通过继承 HttpServlet 类创建 Servlet 时,只需要重写 doGet 或者 doPost 方法,代码如下。

package net.biancheng.www;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用PrintWriter.write()方法向前台页面输出内容
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
    }
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用PrintWriter.write()方法gaifang向前台页面输出内容
        PrintWriter writer = resp.getWriter();
        writer.write("编程帮欢迎您的到来,网址: www.biancheng.net");
        writer.close();
        doGet(req, resp);
    }
}

总结

上面演示了三种创建 Servlet 的方式,那么在实际开发中,我们究竟该选择哪一种呢?下面我们就来分析和对比一下。

  1. Servlet 接口

通过实现 Servlet 接口创建 Servlet 类,需要重写其全部的方法,比较繁琐,所以我们很少使用该方法创建 Servlet。

  1. GenericServlet 类

GenericServlet 抽象类实现了 Servlet 接口,并对 Servlet 接口中除 service() 方法外的其它四个方法进行了简单实现。通过继承 GenericServlet 创建 Servlet,只需要重写 service() 方法即可,大大减少了创建 Servlet 的工作量。

Generic 是“通用”的意思,正如其名,GenericServlet 是一个通用的 Servlet 类,并没有针对某种场景进行特殊处理,尤其是 HTTP 协议,我们必须手动分析和封装 HTPP 协议的请求信息和响应信息。

  1. HttpServlet 类

HttpServlet 是 GenericServlet 的子类,它在 GenericServlet 的基础上专门针对 HTPP 协议进行了处理。HttpServlet 为 HTTP 协议的每种请求方式都提供了对应的方法,名字为 doXxx(),例如:

  • 处理 GET 请求的方法为 doGet();
  • 处理 POST 请求的方法为 doPost()。

正如其名,HttpServlet 就是专为 HTTP 协议而量身打造的 Servlet 类。

在互联网上,人们都是通过 HTTP 协议来访问动态网页的,其中使用最频繁的就是 GET 方式和 POST 方式,因此,我们通常基于 HttpServlet 来创建 Servlet 类,这样就省去了处理 HTTP 请求的过程。

Servlet的部署和访问

Servlet 没有 main() 方法,不能独立运行,但它可以作为 JavaWeb 应用的一个组件被部署到 Servlet 容器中,由容器来实例化和调用 Servlet 的方法,例如:doGet() 、doPost() 等。

那么,JavaWeb 应用是什么呢?Servlet 是如何部署和访问的呢?本节我们将针对这些问题进行讲解。

JavaWeb 应用

JavaWeb 应用由一组 Servlet/JSP、HTML 文件、相关 Java 类、以及其他的资源组成,它可以在由各种供应商提供的 Servlet 容器中运行。由 JavaWeb 应用的定义可知, Servlet 是 JavaWeb 应用的一个组件。

为了让 Servlet 容器顺利地找到 JavaWeb 应用的各个组件,Servlet 规范规定,JavaWeb 应用必须采用固定的目录结构,即每种组件在 JavaWeb 应用中都有固定的存放目录。

以 Tomcat 为例,通常将 JavaWeb 应用存放到 Tomcat 的 webapps 目录下。在 webapps 下,每一个子目录都是一个独立的 Web 应用,子目录的名字就是 Web 应用的名字,也被称为 Web 应用的上下文根。用户可以通过这个上下文根来访问 JavaWeb 应用中的资源。

webapps 的目录结构如下图。

JavaWeb 结构图

下表中对 webapps 下各个目录进行了介绍。

目录描述是否必需
\servletDemoWeb 应用的根目录,属于该 Web 应用的所有资源都存放在这个目录下。
\servletDemo\WEB-INF存放 web.xml、lib 目录以及 classes 目录等。
\servletDemo\WEB-INF\classes存放各种 .class 文件或者包含 .class 文件的目录,Servlet 类的 .class 文件也存放在此。
\servletDemo\WEB-INF\lib存放应用所需的各种 jar 包,例如 JDBC 驱动程序的 jar 包。
\servletDemo\WEB-INF\web.xmlweb.xml 中包含应用程序的配置和部署信息。

部署

在 Tomcat 中部署 JavaWeb 应用最快捷的方式,就是直接将 JavaWeb 应用的所有文件复制到 Tomcat 的 /webapps 目录下。在默认情况下,Tomcat 会自动加载 webapps 目录下的 JavaWeb 应用,并把它发布到名为 localhost 的虚拟主机中

Tomcat 既可以运行采用开放式目录结构(只编译不打包)的 Web 应用,也可以运行 Web 应用的打包文件(WAR 文件)。在开发阶段,为了方便程序调试,通常采用开放式的目录结构部署 JavaWeb 应用。在开发完成,进入产品发布阶段时,就应该将整个应用打包成 WAR 文件,再进行部署。

即使采用 WAR 文件的形式发布,Tomcat 启动时也会将 WAR 文件自动展开为开放式的目录结构。

在本案例中,我们将采用开放式的目录结构进行部署,具体操作步骤如下。

1. 进入 Windows DOS 命令行窗口

我们知道,编译 Java 源代码一般可以通过两种方式完成:DOS 命令行 和 IDE(集成开发环境)。使用 IDE 可以一键完成编译工作,十分的方便,但 IDE 给我们带来方便的同时,也隐藏了编译过程中的许多细节,我们无法了解其中的原理和机制。

对于初学者来说,为了更加深刻地理解 Servlet 的运行原理和机制,最好的办法就是使用 DOS 命令行进行编译。

使用快捷键 “Windows + R”,打开运行对话框,在对话框输入cmd,点击“确定”,进入 Windows 系统命令行窗口,如图。

cmd

2. 引入 javax.servlet 包

由于 Servlet 是 JavaEE 下的技术标准,不是 JDK 的组成部分,所以在编译 Servlet 前,需要先引入 servlet-api.jar 包(在 Apache Tomcat 安装目录下的 lib 文件夹中提供了此 jar 包)。

使用命令set classpath可以将 servlet-api.jar 引入,该命令的语法如下,其中 path 表示引入 jar 包的路径。

set classpath = path

为了方便操作,可以先在命令行中输入set classpath=,然后将 servlet-api.jar 文件拖进命令行内,回车执行。

set calsspath

注:set classpath命令用来为当前窗口设置临时环境变量,只在当前窗口下有效。

3. 编译 Servlet

1)在命令提示符中使用 cd命令进入 MyServlet.java 所在的目录。

servlet cd

2)使用 javac -encoding UTF-8 -d . MyServlet.java命令进行编译,若没有报错,则编译成功。

img

其中:

  • javac :编译命令;
  • -encoding UTF-8:用于指定编译源文件时的字符编码,这里指定为 UTF-8;
  • javac -d:带包编译 ;
  • .:表示当前位置。

3)进入 MyServlet.java 文件所在的目录,可发现新增了编译后的目录和 .classes 字节码文件,自此我们就完成了对 Servlet 的编译工作。

img

4. 创建目录结构

在完成对 Servlet 的编译后,下一步依照 JavaWeb 应用的固定目录结构,在 Tomcat 中为该 Servlet 创建目录。

  1. 在 Tomcat 的 webapps 目录中创建 servletDemo(web 应用的名字,由自己定义)文件夹;
  2. 在 servletDemo 文件夹内创建 WEB-INF 文件夹;
  3. 在 WEB-INF 内创建 classes 文件夹;
  4. 在 WEB-INF 内创建配置文件 web.xml(可以复制 \webapps\ROOT\WEB-INF 中的 web.xml 使用)。
5. 将 Servlet 移动到 Tomcat 目录中

将编译好的字节码和目录移动到 Tomcat\webapps\servletDemo\WEB-INF\classes 目录下。

img

6. 配置 web.xml

对 webapps\servletDemo\WEB-INF 目录的 web.xml 中进行配置,具体配置代码如下。

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"version="3.0" metadata-complete="true">    <servlet>        <servlet-name>MyServlet</servlet-name>        <servlet-class>net.biancheng.www.MyServlet</servlet-class>    </servlet>   <servlet-mapping>        <servlet-name>MyServlet</servlet-name>        <url-pattern>/MyServlet</url-pattern>    </servlet-mapping></web-app>

web.xml 中各元素含义及用法如下:

  • : 根元素。
  • :用于注册 Servlet,即给 Servlet 起一个独一无二的名字。
  • 包含两个主要的子元素 和 ,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。
  • :用于定义 Servlet 与 URL 之间的映射。
  • 包含两个子元素 和 ,分别用于指定 Servlet 的名称和虚拟路径。

访问

进入 Tomcat\bin 目录下双击 startup.bat,启动 Tomcat。

tomcat 启动

Tomcat 启动成功后,在地址栏中输入“http://localhost:8080/servletDemo/MyServlet”,访问 MyServlet,结果如下图。

img

访问路径 http://localhost:8080/servletDemo/MyServlet 中,各部分含义如下:

  • http:// 表示 HTTP 协议;
  • localhost: 表示服务器 IP;
  • 8080 表示端口号;
  • /servletDemo 表示 Web 应用的上下文根路径;
  • /MyServlet 表示资源路径,即 web.xml 中 元素的取值。

@WebServlet注解(Servlet注解)

在 Servlet 中,web.xml 扮演的角色十分的重要,它可以将所有的 Servlet 的配置集中进行管理,但是若项目中 Servelt 数量较多时,web.xml 的配置会变得十分的冗长。这种情况下,注解(Annotation)就是一种更好的选择。

与 XML 不同,注解不需要依赖于配置文件,它可以直接在类中使用,其配置只对当前类有效,这样就避免了集中管理造成的配置冗长问题。那么 Servelt 支持注解吗?

为了简化 Servlet 的配置,Servlet 3.0 中增加了注解支持,例如:@WebServlet、@WebInitParm 、@WebFilter 和 @WebLitener 等,这使得 web.xml 从 Servlet 3.0 开始不再是必选项了。下面我们对 @WebServlet 进行介绍。

@WebServlet 注解的属性

@WebServlet 用于将一个类声明为 Servlet,该注解会在部署时被容器处理,容器根据其具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性。

属性名类型标签描述是否必需
nameString指定 Servlet 的 name 属性。 如果没有显式指定,则取值为该 Servlet 的完全限定名,即包名+类名。
valueString[ ]该属性等价于 urlPatterns 属性,两者不能同时指定。 如果同时指定,通常是忽略 value 的取值。
urlPatternsString[ ]指定一组 Servlet 的 URL 匹配模式。
loadOnStartupint指定 Servlet 的加载顺序。
initParamsWebInitParam[ ]指定一组 Servlet 初始化参数。
asyncSupportedboolean声明 Servlet 是否支持异步操作模式。
descriptionString指定该 Servlet 的描述信息。
displayNameString指定该 Servlet 的显示名。

@WebServlet 注解的使用

1. 启用注解支持

web.xml 的顶层标签 中有一个属性:metadata-complete,该属性用于指定当前 web.xml 是否是完全的。若该属性设置为 true,则容器在部署时将只依赖 web.xml,忽略所有的注解。若不配置该属性,或者将其设置为 false,则表示启用注解支持。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <!-- metadata-complete取值为true,表示关闭注解支持 -->
    <!-- metadata-complete取值为false,表示启用注解支持 -->
</web-app>

由于 metadata-complete 属性的默认值是 false,即默认启用 Servlet 注解支持,所以默认情况下,使用该注解时,不必创建 web.xml 文件。

2. 使用 @WebServlet 注解

@WebServlet 属于类级别的注解,标注在继承了 HttpServlet 的类之上。常用的写法是将 Servlet 的相对请求路径(即 value)直接写在注解内,如下所示。

@WebServlet("/MyServlet") 

该写法省略了 urlPatterns 属性名,其完整的写法如下所示。

@WebServlet(urlPatterns = "/MyServlet")。

如果 @WebServlet 中需要设置多个属性,则属性之间必须使用逗号隔开,如下所示。

package net.biancheng.www;
import java.io.IOException;
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;
@WebServlet(asyncSupported = true, name = "myServlet", description = "name描述", loadOnStartup = 1, urlPatterns = {
        "/MyServlet", "/\*" }, initParams = {
                @WebInitParam(name = "编程帮", value = "www.biancheng.net", description = "init参数1"),
                @WebInitParam(name = "京东", value = "www.jd.com", description = "init参数2") })
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
}

注意事项:

  • 通过实现 Serlvet 接口或继承 GenericServlet 创建的 Servlet 类无法使用 @WebServlet 注解。
  • 使用 @WebServlet 注解配置的 Servlet 类,不要在 web.xml 文件中再次配置该 Servlet 相关属性。若同时使用 web.xml 与 @WebServlet 配置同一 Servlet 类,则 web.xml 中 的值与注解中 name 取值不能相同,否则容器会忽略注解中的配置。

@WebServlet 注解 和 web.xml 的优缺点

使用 web.xml 或 @WebServlet 注解都可以配置 Servlet, 两者各有优缺点。

@WebServlet 注解配置 Servlet

优点:@WebServlet 直接在 Servlet 类中使用,代码量少,配置简单。每个类只关注自身业务逻辑,与其他 Servlet 类互不干扰,适合多人同时开发。

缺点:Servlet 较多时,每个 Servlet 的配置分布在各自的类中,不便于查找和修改。

web.xml 配置文件配置 Servlet

优点:集中管理 Servlet 的配置,便于查找和修改。

缺点:代码较繁琐,可读性不强,不易于理解。

Servlet生命周期(附带实例)

生命周期是指事物从创建到毁灭的过程。人的生命周期就是从出生到死亡的过程,在这个过程中,必定会有一些与生命周期息息相关的事件,如:出生、吃饭、上学、死亡等。这些事件会在生命周期中的某个特定时刻发生。

Servlet 也有生命周期,Servlet 的生命周期就是 Servlet 从创建到销毁的过程。Servlet 的生命周期由 Servlet 容器管理,主要分为以下 3 个阶段。

  1. 初始化阶段
  2. 运行时阶段
  3. 销毁阶段

在 javax.servlet.Servlet 接口中定义了 3 个方法:init()、service()、destory(),它们分别在 Servlet 生命周期的不同阶段被 Servlet 容器调用。

初始化阶段

Servlet 初始化是其生命周期的第一个阶段,也是其他阶段的基础。只有完成了初始化,Servlet 才能处理来自客户端的请求。

Servlet 初始化阶段分为 2 步:

  1. 加载和实例化 Servlet;
  2. 调用 init() 方法进行初始化。
1. 加载和实例化 Servlet

Servlet 容器负责加载和实例化 Servlet。当容器启动或首次请求某个 Servlet 时,容器会读取 web.xml 或 @WebServlet 中的配置信息,对指定的 Servlet 进行加载。加载成功后,容器会通过反射对 Servlet 进行实例化。

因为 Servlet 容器是通过 Java 的反射 API 来创建 Servlet 实例的,需要调用 Servlet 的默认构造方法(default constructor,即不带参数的构造方法),所以在编写 Servlet 类时,不能只提供一个带参数的构造方法。

2. 调用 init() 方法进行初始化

加载和实例化完成后,Servlet 容器调用 init() 方法初始化 Servlet 实例。

初始化的目的:让 Servlet 实例在处理请求之前完成一些初始化工作,例如建立数据库连接,获取配置信息等。

在 Servlet 的整个生命周期内,init() 方法只能被调用一次。

初始化期间,Servlet 实例可以通过 ServletConfig 对象获取在 web.xml 或者 @WebServlet 中配置的初始化参数。

运行时阶段

运行时阶段是 Servlet 生命周期中最重要的阶段。Servlet 容器接收到来自客户端请求时,容器会针对该请求分别创建一个 ServletRequst 对象和 ServletResponse 对象,将它们以参数的形式传入 service() 方法内,并调用该方法对请求进行处理。

这里需要注意的是,执行 service() 方法前,init() 方法必须已成功执行。

在 service() 方法中,Servlet 通过 ServletRequst 对象获取客户端的相关信息和请求信息。在请求处理完成后,通过 ServletResponse 对象将响应信息进行包装,返回给客户端。当 Servlet 容器将响应信息返回给客户端后,ServletRequst 对象与 ServletResponse 对象就会被销毁。

在 Servlet 的整个生命周期内,对于 Servlet 的每一次请求,Servlet 容器都会调用一次 service() 方法,并创建新的 ServletRequest 和 ServletResponse 对象。即 service() 方法在 Servlet 的整个生命周期中会被调用多次。

销毁阶段

当 Servlet 容器关闭、重启或移除 Servlet 实例时,容器就会调用 destory() 方法,释放该实例使用的资源,例如:关闭数据库连接,关闭文件的输入流和输出流等,随后该实例被 Java 的垃圾收集器所回收。

对于每个 Servlet 实例来说,destory() 方法只能被调用一次。

Servlet 生命周期执行流程

Servlet 生命周期流程如下图所示。

Servlet 生命周期

在 Servlet 的整个生命周期中,创建 Servlet 实例、init() 方法和 destory() 方法都只执行一次。当初始化完成后,Servlet 容器会将该实例保存在内存中,通过调用它的 service() 方法,为接收到的请求服务。

示例

下面通过一个案例加深对 Servlet 生命周期的理解。

\1. 在 servletDemo 项目中,对 MyServlet.java 进行修改,代码如下:

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
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;
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private int initCount = 0;
    private int httpCount = 0;
    private int destoryCount = 0;
    @Override
    public void destroy() {
        destoryCount++;
        super.destroy();
        // 向控制台输出destory方法被调用次数
        System.out.println(
                "\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*destroy方法:" + destoryCount + "\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
    }
    @Override
    public void init() throws ServletException {
        initCount++;
        super.init();
        // 向控制台输出init方法被调用次数
        System.out.println("init方法:" + initCount);
    }
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        httpCount++;
        // 控制台输出doGet方法次数
        System.out.println("doGet方法:" + httpCount);
        // 设置返回页面格式与字符集
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("初始化次数:" + initCount + "<br/>" + "处理请求次数:" + httpCount + "<br/>" + "销毁次数:" + destoryCount);
        writer.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
}

\2. 启动 Tomcat,在地址栏输入“http://localhost:8080/servletDemo/MyServlet”,多次访问 MyServlet,结果如下图。

Servlet生命周期

\3. 控制台输出,如下图。

servlet生命周期1

\4. 关闭 Tomcat 服务器,控制台输出如下图。

Servlet 生命周期销毁

load-on-startup元素:控制Servlet启动优先级

load-on-startup 是 web.xml 中的一个节点,是 servlet 元素的子元素,用来标记 Servlet 容器启动时是否初始化当前 Servlet,以及当前 Servlet 的初始化顺序。

load-on-startup 元素取值规则如下:

  1. 它的取值必须是一个整数;
  2. 当值小于 0 或者没有指定时,则表示容器在该 Servlet 被首次请求时才会被加载;
  3. 当值大于 0 或等于 0 时,表示容器在启动时就加载并初始化该 Servlet,取值越小,优先级越高;
  4. 当取值相同时,容器就会自行选择顺序进行加载。

@WebServlet 注解的 loadOnStartup 属性与 web.xml 中的 load-on-startup 元素相对应,取值的规则和含义相同。

示例

下面通过一个案例进行演示。

在 servletDemo 项目的 net.biancheng.www 包中,创建名称为 MyServlet1 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
    @Override
    public void destroy() {
        System.out.println(this.getServletName() + ":销毁");
    }
    @Override
    public void init() throws ServletException {
        System.out.println(this.getServletName() + ":初始化完成");
    }
}

web.xml 的配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet1</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1</servlet-class>
        <!-- load-on-startup 取值0 -->
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>MyServlet2</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1</servlet-class>
        <!-- load-on-startup 取值1 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>MyServlet3</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1</servlet-class>
        <!-- load-on-startup 取值2 -->
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>MyServlet4</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1</servlet-class>
        <!-- load-on-startup 取值-1-->
        <load-on-startup>-1</load-on-startup>
    </servlet>
    <!--不设置 load-on-startup ,去默认值 -->
    <servlet>
        <servlet-name>MyServlet5</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet1</servlet-name>
        <url-pattern>/MyServlet1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet2</servlet-name>
        <url-pattern>/MyServlet2</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet3</servlet-name>
        <url-pattern>/MyServlet3</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet4</servlet-name>
        <url-pattern>/MyServlet4</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet5</servlet-name>
        <url-pattern>/MyServlet5</url-pattern>
    </servlet-mapping>
</web-app>

启动 Tomcat 服务器,控制台输出如下图所示。

load-on-start 1

在地址栏输入“http://localhost:8080/servletDemo/MyServlet1”,访问 MyServlet1,结果如下图。

load-on-start 1

在地址栏输入“http://localhost:8080/servletDemo/MyServlet2”,访问 MyServlet2,结果如下图。

load-on-start 1

在地址栏输入“http://localhost:8080/servletDemo/MyServlet3”,访问 MyServlet3,结果如下图。

load-on-start 1

在地址栏输入“http://localhost:8080/servletDemo/MyServlet4”,访问 MyServlet4,结果如下图。

load-on-start servlet 4

在地址栏输入“http://localhost:8080/servletDemo/MyServlet5”,访问 MyServlet5,结果如下图。

load-on-start 5

由示例可知:

  • 由于 MyServlet1、MyServlet2 和 MyServlet3 的 load-on-startup 元素取值都大于等于 0,因此当 Tomcat 启动时,就对它们进行了初始化。
  • 由于在 Servlet 的生命周期内,init() 方法(初始化方法)只能被调用一次,因此当 Tomcat 启动完成后,第一次访问 MyServlet1、MyServlet2 和 MyServlet3 时,它们不会再次被初始化。
  • 由于 MyServlet4 的 load-on-startup 元素取值为负数,因此只有当第一次请求它时,才会进行初始化。
  • 由于 MyServlet5 没有指定 load-on-startup 元素取值,因此只有当第一次请求它时,才会进行初始化。

Servlet虚拟路径映射

客户端通过 URL 地址来访问 Web 服务器中的资源,Servlet 程序若想被外界访问,就必须被映射到一个 URL 地址上。很多时候,该 URL 地址和 Servlet 程序的物理路径(在硬盘上的存储位置)并不一致,因此它被称为虚拟路径。Servlet 与虚拟路径的对应关系就叫做 Servlet 虚拟路径映射。

Servlet 虚拟路径映射可以被分为 2 类:

  1. 单一映射
  2. 多重映射

下面介绍如何实现单一映射和多重映射。

Servlet 单一映射

Servelt 单一映射是指一个 Servlet 只被映射到一个虚拟路径上。

Servlet 单一映射的实现方式有 2 种:

  1. 使用 web.xml 实现单一映射;
  2. 使用 @WebServlet 实现单一映射。
1. web.xml 实现单一映射

在 web.xml 文件中,使用 和 元素实现 Servlet 单一映射,代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>
</web-app>

对以上标签说明如下:

  • 元素用于注册 Servlet,即给 Servlet 起一个独一无二的名字。
  • 包含两个主要的子元素 和 ,分别用于指定 Servlet 的名称和 Servlet 的完整限定名(包名+类名)。
  • 元素用于定义 Servlet 与虚拟路径之间的映射。
  • 包含两个子元素 和 ,分别用于指定 Servlet 的名称和虚拟路径。
2. @WebServlet 实现单一映射

在 @WebServlet 注解中,一般使用 value 属性实现 Servlet 单一映射,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

也可以使用 urlPatterns 属性实现 Servlet 单一映射,代码如下。

package net.biancheng.www;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

Servlet 多重映射

Servlet 的多重映射是指一个 Servlet 可以被映射到多个虚拟路径上。此时,客户端可以通过多个路径实现对同一个 Servlet 的访问。

Servlet 多重映射的实现方式有 3 种:

  1. 配置多个 元素。
  2. 配置多个 子元素。
  3. 在 @WebServlet 的 urlPatterns 属性中使用字符串数组
1. 配置多个 元素

Servlet 2.5 规范之前, 元素只允许包含一个 子元素,若要实现 Servet 的多重映射,只能通过配置多个 元素实现。

以 serveltDemo 为例,在 web.xml 中配置两个 元素,代码如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet2</url-pattern>
    </servlet-mapping>
</web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/myServlet2”,也可以访问 MyServlet,结果如下图。

img

2. 配置多个 子元素

从 Servlet 2.5 开始, 元素可以包含多个 子元素,每个 代表一个虚拟路径的映射规则。因此,通过在一个 元素中配置多个 子元素,也可以实现 Servlet 的多重映射。

以 servletDemo 为例, 元素下添加两个 子元素,代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
        <url-pattern>/myServlet3</url-pattern>
    </servlet-mapping>
</web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/myServlet3”,也可以访问 MyServlet,结果如下图。

img

3. @WebServlet 实现多重映射

Servlet 3.0 增加了对 @WebServlet 注解的支持,我们可以在 urlPatterns 属性中,以字符串数组的形式指定一组映射规则来实现 Servlet 的多重映射。

以 servletDemo 为例,在 @WebServlet 注解的 urlPatterns 属性中添加一组虚拟路径,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
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;
@WebServlet(
        urlPatterns = { "/myServlet", "/myServlet4" })
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private int initCount = 0;
    private int httpCount = 0;
    private int destoryCount = 0;
    @Override
    public void destroy() {
        destoryCount++;
        super.destroy();
        // 向控制台输出destory方法被调用次数
        System.out.println(
                "\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*destroy方法:" + destoryCount + "\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
    }
    @Override
    public void init() throws ServletException {
        initCount++;
        super.init();
        // 向控制台输出init方法被调用次数
        System.out.println("init方法:" + initCount);
    }
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        httpCount++;
        // 控制台输出doGet方法次数
        System.out.println("doGet方法:" + httpCount);
        // 设置返回页面格式与字符集
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("初始化次数:" + initCount + "<br/>" + "处理请求次数:" + httpCount + "<br/>" + "销毁次数:" + destoryCount);
        writer.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    }
}

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/myServlet”,访问 MyServlet,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/myServlet4”,也可以访问 MyServlet,结果如下图。

img

Servlet虚拟路径匹配规则

当 Servlet 容器接收到请求后,容器会将请求的 URL 减去当前应用的上下文路径,使用剩余的字符串作为映射 URL 与 Servelt 虚拟路径进行匹配,匹配成功后将请求交给相应的 Servlet 进行处理。

以 servletDemo 为例,若 URL 为“http://localhost:8080/servletDemo/myServlet”,其应用上下文是 servletDemo,容器会将“http://localhost:8080/servletDemo”去掉,使用剩余的“/myServlet”与 Servlet 虚拟路径进行匹配。

匹配规则

Servlet 虚拟路径匹配规则有以下 4 种:

  1. 完全路径匹配
  2. 目录匹配
  3. 扩展名匹配
  4. 缺省匹配(默认匹配)

下面我们以 servletDemo 为例,分别介绍 4 种规则。

匹配规则使用规则虚拟路径可访问的URL
完全路径匹配 (精确匹配)/开始,不能包含通配符*。 必须完全匹配/myServlet /user/myServlet /product/index.actionhttp://localhost:8080/servletDemo/myServlet http://localhost:8080/servletDemo/user/myServlet http://localhost:8080/servletDemo/product/index.action
目录匹配/字符开头,并以/*结尾的字符串。 用于路径匹配/user/* /*http://localhost:8080/servletDemo/user/aaa http://localhost:8080/servletDemo/aa
扩展名匹配以通配符*.开头的字符串。 用于扩展名匹配*.do *.action *.jsphttp://localhost:8080/servletDemo/user.do http://localhost:8080/servletDemo/myServlet.action http://localhost:8080/servletDemo/bb.jsp
缺省匹配(默认匹配)映射路径为/,表示这个 Servlet 为当前应用的缺省 Servlet 或默认 Servlet,默认处理无法匹配到虚拟路径的请求。/可以匹配任意请求 URL

注意:目录匹配和扩展名匹配无法混合使用,即/rest/*.do这种写法是不正确的。

匹配优先级

Servlet 虚拟路径的匹配优先级顺序为:全路径匹配(精确匹配)> 目录匹配 > 扩展名匹配 > 缺省匹配(默认匹配)。

Servlet 容器会从优先级高的虚拟路径开始匹配,匹配成功后就会立刻将请求交给相应的 Servlet 进行处理,不会再关注其他虚拟路径是否匹配成功。

示例 1

下面我们通过一个实例加深对 Servlet 虚拟路径匹配的理解。

在 servletDemo 的 net.biancheng.www 包下,创建名称为 VirtualPathServlet 的 Servlet 类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class VirtualPathServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        // 向页面输出
        writer.write("本次访问的Servlet是:" + this.getServletName());
        writer.close();
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml 的配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet1</servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>MyServlet2</servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>MyServlet3</servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>MyServlet4</servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>MyServlet5</servlet-name>
        <servlet-class>net.biancheng.www.VirtualPathServlet</servlet-class>
    </servlet>
    <!-- 完全路径匹配 -->
    <servlet-mapping>
        <servlet-name>MyServlet1</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <!-- 目录匹配 -->
    <servlet-mapping>
        <servlet-name>MyServlet2</servlet-name>
        <url-pattern>/abc/my/*</url-pattern>
    </servlet-mapping>
    <!-- 目录匹配 -->
    <servlet-mapping>
        <servlet-name>MyServlet3</servlet-name>
        <url-pattern>/abc/*</url-pattern>
    </servlet-mapping>
    <!-- 扩展名匹配 -->
    <servlet-mapping>
        <servlet-name>MyServlet4</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <!--缺省匹配 -->
    <servlet-mapping>
        <servlet-name>MyServlet5</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

启动 Tomcat 服务器,在地址栏中输入“http://localhost:8080/servletDemo/hello”,结果如下图。

/hello

在地址栏中输入“http://localhost:8080/servletDemo/abc/my/login”,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/abc/index.do”,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/hello/index.do”,结果如下图。

img

在地址栏中输入“http://localhost:8080/servletDemo/hello/pay.html”,结果如下图。

/abc/index.do

Tomcat 中的缺省 Servlet

在 Tomcat 安装目录的 \conf\web.xml 文件中,注册了一个名称为 org.apache.catalina.servlets.DefaultServlet 的 Servlet,并将它设置为缺省 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>

Tomcat 服务器中的 Web 应用没有缺省 Servlet 时,会将 DefaultServlet 作为其缺省 Servlet。当客户端访问 Tomcat 服务器中某个静态 HTML 文件或者图片时,DefaultServlet 会判断该 HTML 或图片是否存在,若存在,则将数据以流的形式返回客户端,否则会报告 404 错误。

示例 2

将 servletDemo 应用的缺省匹配删除,并在 servletDemo 应用里添加一张图片 a.jpg。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <!--使用tomcat的缺省Servlet访问静态资源或图片 -->
    <!--需要先将项目内的缺省Servlet删除 -->
    <!--缺省匹配 -->
<!--<servlet-mapping>
 <servlet-name>MyServlet5</servlet-name>
 <url-pattern>/</url-pattern>
 </servlet-mapping>-->
</web-app>

启动 Tomcat,在地址栏输入“http://localhost:8080/servletDemo/a.jpg”,结果如下图。

缺省模式

ServletConfig接口详解

Servlet 容器初始化 Servlet 时,会为这个 Servlet 创建一个 ServletConfig 对象,并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。

一个 Web 应用中可以存在多个 ServletConfig 对象,一个 Servlet 只能对应一个 ServletConfig 对象,即 Servlet 的初始化参数仅对当前 Servlet 有效。

获得 ServletConfig 对象

获得 ServletConfig 对象一般有 2 种方式:

1. 直接从带参的 init() 方法中提取
public class ServletConfigDemo extends HttpServlet {
    private ServletConfig servletConfig;
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Servlet得名字
        this.servletConfig.getServletName();
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        //从带参init方法中,提取ServletConfig对象
        this.servletConfig = config;
    }
}

2. 调用 GenericServlet 提供的 getServletConfig() 方法获得
//调用 GenericServlet 提供的 getServletConfig 方法获得 ServletConfig 对象ServletConfig servletConfig = this.getServletConfig();

ServletConfig 接口

javax.servlet 包提供了一个 ServletConfig 接口,该接口中提供了以下方法。

返回值类型方法功能描述
StringgetInitParameter(String name)根据初始化参数名 name,返回对应的初始化参数值。
EnumerationgetInitParameterNames()返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。
ServletContextgetServletContext()返回一个代表当前 Web 应用的 ServletContext 对象。
StringgetServletName()返回 Servlet 的名字,即 web.xml 中 元素的值。

配置 Servlet 初始化参数

配置 Servlet 的初始化参数有 2 种方式:

  1. 使用 web.xml 配置初始化参数;
  2. 使用 @WebServlet 配置初始化参数。
1.使用 web.xml 配置初始化参数

在 web.xml 中可以使用一个或多个 元素为 Servlet 配置初始化参数,代码如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app\_4\_0.xsd"
 id="WebApp\_ID" metadata-complete="false" version="4.0">
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>net.biancheng.www.MyServlet</servlet-class>
        <!-- Servlet 初始化参数 -->
        <init-param>
            <param-name>name</param-name>
            <param-value>编程帮</param-value>
        </init-param>
        <!-- Servlet 初始化参数 -->
        <init-param>
            <param-name>URL</param-name>
            <param-value>www.biancheng.net</param-value>
        </init-param>
    </servlet>
</web-app>

以上配置说明如下:

  • 元素是 的子元素, 需要在 元素内使用,表示只对当前 Servlet 有效 。
  • 子元素表示参数的名称。
  • 子元素表示参数的值。
2.使用 @WebServlet 配置初始化参数

通过 @WebServlet 的 initParams 属性也可以为 Servlet 设置初始化参数,代码如下。

package net.biancheng.www;
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;
@WebServlet(urlPatterns = {"/MyServlet"}, initParams = {@WebInitParam(name = "name", value = "编程帮"),
        @WebInitParam(name = "URL", value = "www.biancheng.net")})
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

获取 Servlet 初始化参数

下面我们通过一个例子演示如何通过 ServletConfig 对象读取 Servlet 的初始化参数。

以 servletDemo 工程为例,在 net.biancheng.www 包下,创建名称为 ReadConfigServlet 的类,代码如下。

package net.biancheng.www;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
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;
/\*\*
\*
\* @author 编程帮 www.biancheng.net
\* 获取Servlet的初始化参数
\*
\*/
@WebServlet(urlPatterns = { "/ReadConfigServlet" }, initParams = { @WebInitParam(name = "name", value = "编程帮"),
        @WebInitParam(name = "URL", value = "www.biancheng.net") })
public class ReadConfigServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        // 获取ServletConfig对象
        ServletConfig config = getServletConfig();
        // 获取servletName
        String servletName = config.getServletName();
        // 返回 servlet 的初始化参数的名称的集合
        Enumeration<String> initParameterNames = config.getInitParameterNames();
        // 遍历集合获取初始化参数名称
        while (initParameterNames.hasMoreElements()) {
            // 获取初始化参数名称
            String initParamName = initParameterNames.nextElement();
            // 获取相应的初始参数的值
            String initParamValue = config.getInitParameter(initParamName);
            // 向页面输出
            writer.write(initParamName + " : " + initParamValue + "<br/>");
        }
        // 关闭流
        writer.close();
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

启动 Tomcat 服务器,在地址栏输入“http://localhost:8080/servletDemo/ReadConfigServlet”,访问 ReadConfigServlet,结果如下图。

ServletConfig

ServletContext接口详解

Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。

ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。

Web 应用中的所有 Servlet 共享同一个 ServletContext 对象,不同 Servlet 之间可以通过 ServletContext 对象实现数据通讯,因此 ServletContext 对象也被称为 Context 域对象。

域对象是服务器在内存上创建的存储空间,该空间用于不同动态资源(例如 Servlet、JSP)之间传递与共享数据。

获得 ServletContext 对象

获得 ServletContext 对象有以下 4 种方式:

1. 通过 GenericServlet 提供的 getServletContext() 方法
//通过 GenericServlet的getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletContext();

2. 通过 ServletConfig 提供的 getServletContext() 方法
//通过 ServletConfig的 getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletConfig().getServletContext();

3. 通过 HttpSession 提供的 getServletContext() 方法
//通过 HttpSession的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getSession().getServletContext();

4. 通过 HttpServletRequest 提供的 getServletContext() 方法
//通过 HttpServletRequest的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getServletContext();

注意:以上最后两种方法了解即可,后面我们会详细讲解。

ServletContext 的应用

javax.servlet 包提供了一个 ServletContext 接口,该接口定义了一组方法,Servlet 可以使用这些方法与容器进行通信。

ServletContext 的应用主要有以下 3 个:

  1. 获取上下文初始化参数
  2. 实现 Servlet 之间的数据通讯
  3. 读取 Web 应用下的资源文件
1. 获取上下文初始化参数

使用 ServletContext 对象获取 Web 应用的上下文初始化参数,分为 2 步:

  1. 设置上下文初始化参数
  2. 调用接口中方法获取初始化参数
1) 设置上下文初始化参数

通过 web.xml 中的 元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。

与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。

面试题总结

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

tp://localhost:8080/servletDemo/ReadConfigServlet”,访问 ReadConfigServlet,结果如下图。

ServletConfig

ServletContext接口详解

Servlet 容器启动时,会为每个 Web 应用(webapps 下的每个目录都是一个 Web 应用)创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文”。

ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。

Web 应用中的所有 Servlet 共享同一个 ServletContext 对象,不同 Servlet 之间可以通过 ServletContext 对象实现数据通讯,因此 ServletContext 对象也被称为 Context 域对象。

域对象是服务器在内存上创建的存储空间,该空间用于不同动态资源(例如 Servlet、JSP)之间传递与共享数据。

获得 ServletContext 对象

获得 ServletContext 对象有以下 4 种方式:

1. 通过 GenericServlet 提供的 getServletContext() 方法
//通过 GenericServlet的getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletContext();

2. 通过 ServletConfig 提供的 getServletContext() 方法
//通过 ServletConfig的 getServletContext方法获取ServletContext对象ServletContext servletContext = this.getServletConfig().getServletContext();

3. 通过 HttpSession 提供的 getServletContext() 方法
//通过 HttpSession的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getSession().getServletContext();

4. 通过 HttpServletRequest 提供的 getServletContext() 方法
//通过 HttpServletRequest的 getServletContext方法获取ServletContext对象ServletContext servletContext = req.getServletContext();

注意:以上最后两种方法了解即可,后面我们会详细讲解。

ServletContext 的应用

javax.servlet 包提供了一个 ServletContext 接口,该接口定义了一组方法,Servlet 可以使用这些方法与容器进行通信。

ServletContext 的应用主要有以下 3 个:

  1. 获取上下文初始化参数
  2. 实现 Servlet 之间的数据通讯
  3. 读取 Web 应用下的资源文件
1. 获取上下文初始化参数

使用 ServletContext 对象获取 Web 应用的上下文初始化参数,分为 2 步:

  1. 设置上下文初始化参数
  2. 调用接口中方法获取初始化参数
1) 设置上下文初始化参数

通过 web.xml 中的 元素可以为 Web 应用设置一些全局的初始化参数,这些参数被称为上下文初始化参数。

与 Servlet 的初始化参数不同,应用中的所有 Servlet 都共享同一个上下文初始化参数。在 Web 应用的整个生命周期中,上下文初始化参数会一直存在,并且可以随时被任意一个 Servlet 访问。

面试题总结

其它面试题(springboot、mybatis、并发、java中高级面试总结等)

[外链图片转存中…(img-oLwO2Xkq-1714508695202)]

[外链图片转存中…(img-7e8eOVLu-1714508695203)]

[外链图片转存中…(img-x32ELWSg-1714508695203)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值