JavaWeb 学习笔记 3:Servlet

JavaWeb 学习笔记 3:Servlet

1.简介

Servlet 是 JavaEE 定义的一套 Web 应用开发标准(接口),实现了该技术的 Web 服务器软件(如 Tomcat)上可以运行一个 Servlet 容器,只要我们使用 Servlet 技术开发 Web 应用,就可以打成 war 包后放在 Web 服务器上,Web 服务器软件可以自动解包,并执行其中 Servlet 相关的 API 实现类,以对外提供服务。

整个过程可以表示为:

1627234763207

2.快速开始

先按照上篇文章说的,创建一个 Maven Web 项目。

这里我使用 Maven 模板的方式创建项目:

mvn archetype:generate ^
-DgroupId=cn.icexmoon ^
-DartifactId=web-demo ^
-DarchetypeArtifactId=maven-archetype-webapp ^
-Dversion=0.0.1-snapshot ^
-DinteractiveMode=false

这里是 CMD 中执行的多行命令,因为分行符号的不同,不同的终端工具写法有所不同。

会自动生成一个 JUnit 依赖,不过版本太老(3.x),这里替换为 4.x:

<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope>
</dependency>

添加 Tomcat 启动插件:

<plugins><!--Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin>
</plugins>

添加 Servlet API 的依赖:

<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope>
</dependency>

创建一个类,并实现Servlet接口:

public class HelloServlet implements Servlet {public void init(ServletConfig servletConfig) throws ServletException {}public ServletConfig getServletConfig() {return null;}public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("Hello World!");}public String getServletInfo() {return null;}public void destroy() {}
}

其中负责处理 HTTP 请求的是service方法,这里简单的在控制台打印一句话。

新版本的 Servlet 可以使用注解的方式指定请求路径:

@WebServlet("/hello")
public class HelloServlet implements Servlet {// ...
}

访问 http://localhost:8080/web-demo/hello,在服务端控制台可以看到输出。

3.Servlet 生命周期

Servlet 对象由 Web 服务器创建,其中实现的Servlet接口相关方法也由 Web 服务器在适当的时候执行。这些执行时机是和 Servlet 对象的生命周期相关的。

Servlet 对象有以下生命周期:

  • 实例创建:由 Web 服务器创建 Servlet 实例,默认为延迟创建(有相关的 HTTP 请求时才创建)。
  • 初始化:Servlet 对象创建后,Web 服务器会调用 Servlet.init()方法执行初始化工作,一般用于准备 Servlet 处理 HTTP 请求需要的资源等。
  • 处理请求:有 Servlet 相关的 HTTP 请求产生后,Web 服务器会调用Servlet.service()方法处理请求并返回处理结果(HTTP 响应报文)。
  • 销毁:当 Web 服务器(正常)关闭,或者 JVM 执行内存清理时,Web 服务器会调用Servlet.destroy()方法执行清理工作,主要包含对 Servlet 拥有资源的清理和关闭等。

3.1.init

默认情况下 init() 方法只会在第一次 HTTP 请求产生后被调用:

@WebServlet("/hello")
public class HelloServlet implements Servlet {public void init(ServletConfig servletConfig) throws ServletException {System.out.println("Servlet init...");}// ...
}

这是因为我们上边说过的,Servlet 实例创建的方式默认为延迟创建,而初始化是在创建之后执行。

可以将 Servlet 对象的创建方式修改为“急切创建”:

@WebServlet(value = "/hello", loadOnStartup = 1)
public class HelloServlet implements Servlet {// ...
}

属性loadOnStartup决定 Servlet 对象创建的时机:

  • 负数,延迟创建。在第一次相关的 HTTP 请求产生后创建 Servlet 对象。
  • 0 或正整数,急切创建。Web 服务器启动后立即创建 Servlet 对象,数字越小优先级越高。

3.2.service

在有相关的 HTTP 请求产生时被调用。负责处理 HTTP 请求,并生成响应报文。

public class HelloServlet implements Servlet {public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {System.out.println("Hello World!");}
}

3.3.destroy

Web 服务器(正常)关闭,或者 JVM 执行内存清理时被调用。

public class HelloServlet implements Servlet {public void destroy() {System.out.println("Servlet destroy...");}// ...
}

要注意的是,像直接终止 Tomcat 进程这种操作属于非正常关闭,Tomcat 是来不及执行正常的关闭流程的,也就不会执行 Servlet 的清理工作。

因此,要触发destroy方法,需要让 Tomcat 正常退出。其中的一个方法是在命令行下用 Maven 插件启动 Tomcat,并使用热键Ctrl+C退出程序:

D:\workspace\learn-javaweb\ch3\web-demo>mvn tomcat7:run
[INFO] Scanning for projects...
[INFO]
...
九月 08, 2023 11:33:53 上午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-bio-8080"]
Servlet destroy...
终止批处理操作吗(Y/N)? y

4.Servlet 抽象层级

4.1.请求分发

默认情况下,写在 Servlet.service()方法中的内容可以用于处理任意 Request Method 类型的 HTTP 请求。这通常是不合适的,通常我们需要对特定 Reuqest Method 的请求使用特定的处理。

比如,如果要对 http://localhost:8080/web-demo/hello 请求时的 POST 和 GET 方法执行不同的处理逻辑:

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {HttpServletRequest request = (HttpServletRequest) servletRequest;switch (request.getMethod()){case "GET":System.out.println("From get request.");break;case "POST":System.out.println("From post request.");break;default:}System.out.println("Hello World!");
}

测试 POST 请求可以使用 HTTP 调试工具(比如 APIPost)。

4.2.模板模式

如果每个 Servlet 都要这样写,就很麻烦。可以使用模板模式的思想进行重构,抽离出一个 Servlet 的公共基类:

public abstract class HttpServletTemplate implements Servlet {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic final void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {if (!(servletRequest instanceof HttpServletRequest)|| !(servletResponse instanceof HttpServletResponse)) {throw new RuntimeException("Servlet 请求和响应对象不是 HTTP 相关");}HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;switch (request.getMethod()){case "GET":this.doGet(request, response);break;case "POST":this.doPost(request, response);break;default:}}protected void doPost(HttpServletRequest request, HttpServletResponse response) {}protected void doGet(HttpServletRequest request, HttpServletResponse response) {}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}
}

这里有一些细节:

  • 模板类并不需要生成实例,所以被定义为抽象类(abstract)。
  • service方法负责按照 Request Method 类别进行分发,被分发后的方法(比如 doGet)需要被子类继承和重写,所以设置为protected。考虑到灵活性,这里没有将其设置为抽象方法,否则即使不用处理POST请求,也必须重写doPost方法。
  • 分发请求的service()方法不能被子类重写,所以设置为final

现在只需要很少的代码就可以实现之前的要求:

@WebServlet("/hello2")
public class Hello2Servlet extends HttpServletTemplate{@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) {System.out.println("From post request.");System.out.println("Hello World2!");}@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {System.out.println("From get request.");System.out.println("Hello World2!");}
}

可以按照需要覆盖doXXX方法。

4.3.HttpServlet

实际上这种抽象和重构的工作并不需要我们自己完成,Servlet 本身就提供类似的抽象:

image-20230908122518757

因此我们只需要继承HttpServlet就可以了:

@WebServlet("/hello3")
public class Hello3Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);System.out.println("From get request.");System.out.println("Hello World3!");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doPost(req, resp);System.out.println("From post request.");System.out.println("Hello World3!");}
}

5.urlPattern

Servlet 依赖于 @WebServlet注解的urlPatterns属性确定是否与请求路径(url)匹配。

@WebServletvalue属性是urlPatterns属性的别名。

5.1.精确匹配

常见的 urlPattern 都是采用精确匹配:

@WebServlet("/user/list")
public class UserServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);System.out.println("UserServlet...");}
}

此时只有请求路径是/user/list的 HTTP 请求才会被这个 Servlet 处理。

urlPattern 可以配置多个值,此时这些规则都可以用于匹配:

@WebServlet({"/user/list","/user/1"})

现在无论是/user/list这样的请求还是/user/1这样的请求都会被这个 Servlet 处理。

5.2.路径匹配

可以使用通配符(*)匹配某个路径下的请求,比如:

@WebServlet("/user/*")
public class User2Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);System.out.println("User2Servlet...");}
}

示例中的 Servlet 可以处理任意以/user/开头的路径。

需要注意的是,是可以一个路径同时匹配多个 Servlet 的 urlPattern 的,比如目前这个示例中,/user/list这样的请求同时可以被UserServletUser2Servlet的规则匹配。此时精确匹配的优先级高于路径匹配,所以是UserServlet处理请求。

5.3.扩展名匹配

在 url 中同样可以使用扩展名作为请求结尾,比如:

http://localhost:8080/web-demo/book/list.do

可以用下面的 Servlet 匹配和处理:

@WebServlet("*.do")
public class User3Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);System.out.println("User3Servlet...");}
}

需要注意的是,扩展名匹配前不能加路径分隔符/,比如:

@WebServlet("/*.do")

此时应用无法启动,会报错:

java.lang.IllegalArgumentException: Invalid <url-pattern> /*.do in servlet mapping...

5.4.任意匹配

可以用//*匹配任意路径的请求。

@WebServlet("/")
public class User4Servlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {super.doGet(req, resp);System.out.println("User4Servlet...");}
}

上面的示例可以匹配任意没有被其它 Servlet 匹配的请求。

可以同时配置//*匹配,但后者的优先级更高。此外/配置后会替换掉 Tomcat 默认的DefaultServlet,该 Servlet 负责处理对静态资源的请求。换言之,使用//*匹配路径可能导致无法访问静态资源。所以不推荐在项目中使用//*匹配路径。

最后,几种匹配模式的优先级是 精确匹配 > 目录匹配> 扩展名匹配 > /* > / 。

6.用 XML 配置 Servlet

Servlet 从 3.0 版本之后支持以注解的方式进行配置,在 3.0 版本之前需要在web.xml文件中配置。

添加一个不用注解配置的 Servlet:

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

修改 web.xml

<web-app><display-name>Archetype Created Web Application</display-name><servlet><servlet-name>user4servlet</servlet-name><servlet-class>cn.icexmoon.webdemo.urlpattern.User4Servlet</servlet-class></servlet><servlet-mapping><servlet-name>user4servlet</servlet-name><url-pattern>/user/4</url-pattern></servlet-mapping>
</web-app>

和用注解配置的方式效果是相同的。

The End,谢谢阅读。

本文的完整示例可以从这里获取。

7.参考资料

  • 黑马程序员JavaWeb基础教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/82193.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【测试开发】基础篇 · 专业术语 · 软件测试生命周期 · bug的描述 · bug的级别 · bug的生命周期 · 处理争执

【测试开发】基础篇 文章目录 【测试开发】基础篇1. 软件测试生命周期1.1 软件生命周期1.2 软件测试生命周期 2. 描述bug3. 如何定义bug的级别3.1 为什么要对bug进行级别划分3.2 bug的一些常见级别 4. bug的生命周期5. 产生争执这么怎么办&#xff08;处理人际关系&#xff09;…

服务器数据恢复-LINUX操作系统下各文件系统误删除/格式化数据的恢复方案

服务器数据恢复环境&#xff1a; 基于EXT2/EXT3/EXT4/Reiserfs/Xfs文件系统的Linux操作系统。 服务器故障&#xff1a; LINUX操作系统下误删除/格式化数据。 服务器数据恢复过程&#xff1a; 1、首先会检测服务器是否存在硬件故障&#xff0c;如果检测出硬件故障&#xff0c;交…

Golang Linux 安装与环境变量配置

下载 Go 二进制包 wget https://dl.google.com/go/go1.21.1.linux-amd64.tar.gz 解压文件并将其移至 /usr/local 目录 sudo tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz -C 选项解压文件到 /usr/local 目录&#xff0c;查看 /usr/local/go 目录的内容 将 Go 二进…

19 视图定义 union 是根据第一个 select 字段列表顺序,来进行 merge 的

前言 这个问题主要是 在之前存在这样的一个问题, 在生产环境上面 按照 我的直观理解, mysql 应该是根据 key 进行 merge, 所以 select 的顺序应该是 “不重要”??, 但是 结果我理解错了 然后 线上的查询也出现了问题, 发现很奇怪的问题, 明明 key01 列 是 id, 但是有一部…

深度学习——线性神经网络一

深度学习——线性神经网络一 文章目录 前言一、线性回归1.1. 线性回归的基本元素1.1.1. 线性模型1.1.2. 损失函数1.1.3. 解析解1.1.4. 随机梯度下降1.1.5. 用模型进行预测 1.2. 向量化加速1.3. 正态分布与平方损失1.4. 从线性回归到深度网络 二、线性回归的从零开始实现2.1. 生…

Java面试题整理(带答案)

目录 TCP和UDP的区别 get和post的区别 Cookie和session的区别 Java的基本类型有哪些&#xff1f; 抽象类和接口区别&#xff1f; 对于堆栈的理解 和equals区别 如何理解Java多态&#xff1f; 创建线程都有哪些方式 脏读、不可重复度、幻读都是什么&#xff1f; Jav…

多线程详解(上)

文章目录 一、线程的概念1&#xff09;线程是什么2&#xff09;为甚要有线程&#xff08;1&#xff09;“并发编程”成为“刚需”&#xff08;2&#xff09;在并发编程中, 线程比进程更轻量. 3&#xff09;线程和进程的区别 二、Thread的使用1&#xff09;线程的创建继承Thread…

算法通关村-----链表中环的问题

环形链表 问题描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中…

使用Scrapy构建高效的网络爬虫

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 Scrapy是一个强大的Pyth…

python虚拟环境(venv)

一、什么是python环境 首先要知道什么是python环境&#xff1f; Python环境主要包括以下内容&#xff1a; 解释器 python.exe (python interpreter&#xff0c;使用的哪个解释看环境配置) Lib目录 标准库 第三方库&#xff1a;site-pakages目录&#xff0c;默认安装第三方…

基于DSPACE功率平衡理论的并联有源电力滤波器模型(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

webpack配置alias后eslint和ts无法识别

背景 我们在 webpack 配置 alias 后&#xff0c;发现项目中引入的时候&#xff0c;还是会报错&#xff0c;如下&#xff1a; 可以看到&#xff0c;有一个是 ts报错&#xff0c;还有一个是 eslint 报错。 解决 ts 报错 tsconfig.json {"compilerOptions": {...&q…

【力扣每日一题】2023.9.18 打家劫舍Ⅲ

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 今天是打家劫舍3&#xff0c;明天估计就是打家劫舍4了。 今天的打家劫舍不太一样&#xff0c;改成二叉树了&#xff0c;不过规则没有变&…

狗dog 数据集VOC-5912张

狗&#xff0c;是食肉目犬科犬属 哺乳动物 &#xff0c;别称犬&#xff0c;与马、牛、羊、猪、鸡并称“六畜” 。狗的体型大小、毛色因品种不同而不同&#xff0c;体格匀称&#xff1b;鼻吻部较长&#xff1b;眼呈卵圆形&#xff1b;两耳或竖或垂&#xff1b;四肢矫健&#xff…

网站降权的康复办法(详解百度SEO数据分析)

随着搜索引擎算法的不断升级&#xff0c;很多网站在SEO优化过程中遭遇到降权的情况。如果您的网站也遭遇到了类似的问题&#xff0c;不必惊慌失措。本文将为您详细介绍网站降权恢复的方法&#xff0c;包括百度SEO数据分析、网站收录少的5个原因、网站被降权的6个因素以及百度SE…

IP风险查询:抵御DDoS攻击和CC攻击的关键一步

随着互联网的普及&#xff0c;网络攻击变得越来越普遍和复杂&#xff0c;对企业和个人的网络安全构成了重大威胁。其中&#xff0c;DDoS&#xff08;分布式拒绝服务&#xff09;攻击和CC&#xff08;网络连接&#xff09;攻击是两种常见且具有破坏性的攻击类型&#xff0c;它们…

js写一个判断字符串是否能够转为JSON 的函数

其实非常简单 这里我们需要涉及到 捕获异常 因为如果你直接在if里面转 我已经试过了 直接就报错了 一点面子不给 我们写一个这样的函数 function isJsonString(str) {try {JSON.parse(str);return true;} catch (e) {return false;} }编写如下代码 console.log(isJsonString(…

企业架构LNMP学习笔记58

开始学习Tomcat&#xff1a; 学习目标和内容&#xff1a; 1&#xff09;能够描述Tomcat的使用场景&#xff1b; 2&#xff09;能够简单描述Tomcat的工作原理&#xff1b; 3&#xff09;能够实现部署安装Tomcat&#xff1b; 4&#xff09;能够实现和配置Tomcat的Server服务…

VMware虚拟机如何设置网络

一直没弄明白怎么能让虚拟机正常上网和访问&#xff0c;最近总结一个小经验 要在宿主机访问虚拟机电脑服务器&#xff0c;要设置成nat格式&#xff0c;虚拟机可以上网&#xff0c;宿主机访问虚拟机上的ip即可访问虚拟机里的服务器&#xff0c;也就是这样设置就行。 这时候ip不…

blender怎么设置中文界面

你们知道Blender软件是什么吗&#xff1f;你知道blender怎么设置中文界面吗&#xff1f;Blender是个GNU的3D绘图软件&#xff0c;建模、算图、动画等功能都相当的完整&#xff0c;可以说已经具有了一般商业软件的规模。Blender大部分的功能都有热键&#xff0c;操作起来相当地轻…