深入理解Servlet(下)

news2025/1/26 15:51:43
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

在这一篇文章里,将会讨论ServletContext以及Servlet映射规则。这两个知识点非常重要,ServletContext直接关系到SpringIOC容器的初始化,而Servlet映射规则与SpringMVC关系密切。

可以说,作为初学者只要把这两点搞清楚,那么对Spring/SpringMVC的理解将会超过70%的程序员。我没开玩笑,你随便抓一个身边的同事问问,Tomcat和Spring什么关系?SpringMVC和Servlet什么关系?估计没几个能给你讲清楚的。

我会先把SpringMVC讲得很简单,等大家觉得它就是个Servlet的时候,我又会把SpringMVC慢慢展开,露出它的全貌。此时你又会发现:SpringMVC is not only a Servlet.

主要内容:

  • ServletContext是什么
  • 如何获取ServletContext
  • Filter拦截方式之:REQUEST/FORWARD/INCLUDE/ERROR
  • Servlet映射器
  • 自定义DispatcherServlet
  • DispatcherServlet与SpringMVC
  • conf/web.xml与应用的web.xml

ServletContext是什么

ServletContext,直译的话叫做“Servlet上下文”,听着挺别扭,但其实就是个大容器,是一个map。服务器会为每个应用创建一个ServletContext对象:

  • ServletContext对象的创建是在服务器启动时完成的
  • ServletContext对象的销毁是在服务器关闭时完成的

ServletContext对象的作用是在整个Web应用的动态资源(Servlet/JSP)之间共享数据。例如在AServlet中向ServletContext对象保存一个值,然后在BServlet中就可以获取这个值。

这种用来装载共享数据的对象,在JavaWeb中共有4个,而且更习惯被成为“域对象”:

  • ServletContext域(Servlet间共享数据)
  • Session域(一次会话间共享数据,也可以理解为多次请求间共享数据)
  • Request域(同一次请求共享数据)
  • Page域(JSP页面内共享数据)

它们都可以看做是map,都有getAttribute()/setAttribute()方法。来看一下物理磁盘中的配置文件与内存对象之间的映射关系:

每一个动态web工程,都应该在WEB-INF下创建一个web.xml,它代表当前整个应用。Tomcat会根据这个配置文件创建ServletContext对象

如何获取ServletContext

还记得GenericServlet吗?它在init方法中,将Tomcat传入的ServletConfig对象的作用域由局部变量(方法内使用)提升到成员变量。并且新建了一个getServletContext():

getServletContext()内部其实就是config.getServletContext()

也就是说ServletConfig对象可以得到ServletContext对象。但是这并不意味这ServletConfig对象包含着ServletContext对象,而是ServletConfig维系着ServletContext的引用。

其实这也很好理解:servletConfig是servletContext的一部分,就像他儿子。你问它父亲是谁,它当然能告诉你。

另外,Session域和Request域也可以得到ServletContext:

session.getServletContext();
request.getServletContext();

本质就是,用域对象获得域对象

最后,还有个冷门的,我在监听器那一篇讲过了:

事件对象就是对事件源(被监听对象)的简单包装

所以,获取ServletContext的方法共5种(page域这里不考虑,JSP太少用了):

  • ServletConfig#getServletContext()
  • GenericServlet#getServletContext()
  • HttpSession#getServletContext()
  • HttpServletRequest#getServletContext()
  • ServletContextEvent#getServletContext()

Filter拦截方式之:REQUEST/FORWARD/INCLUDE/ERROR

在很多人眼里,Filter只能拦截Request:

这样的理解还是太片面了。

其实配置Filter时可以设置4种拦截方式:

很多人要么不知道,要么理解得不够清晰。

这里,我先帮大家把这4种方式和重定向(Redirect)剥离开,免得有人搞混,它们是完全两类(前者和Request有关,后者通过Response发起),以FORWARD为例:

蓝色:重定向,橙色:转发

我们日常开发中,FORWARD用的最多的场景就是转发给JSP,然后模板输出HTML。

Redirect和REQUEST/FORWARD/INCLUDE/ERROR最大区别在于:

重定向会导致浏览器发送2次请求,FORWARD们是服务器内部的1次请求

了解这个区别之后,我提一个很奇怪的问题:为什么这4种只引发1次请求?

是不是听傻了?我接下来给的答案,属于意料之外情理之中的那种:

因为FORWARD/INCLUDE等请求的分发是服务器内部的流程,不涉及浏览器

还记得如何转发吗:

我们发现通过Request或者ServletContext都可以得到分发器Dispatcher,但由于ServletContext代表整个应用,我更倾向于认为:ServletContext拥有分发器,Request是找它借的。

分发器是干嘛的?分发请求:REQUEST/FORWARD/INCLUDE/ERROR。REQUEST是浏览器发起的,而ERROR是发生页面错误时发生的,稍微特殊些。

所以,所谓Filter更详细的拦截其实是这样:

灰色块:Filter

最外层那个圈,可以理解成ServletContext,FORWARD/INCLUDE这些都是内部请求。如果在web.xml中配置Filter时4种拦截方式全配上,那么服务器内部的分发跳转都会被过滤。

当然,这些都是可配置的,默认只拦截REQUEST,也就是浏览器来的那一次。

Servlet映射器

上一篇说了很多Servlet的源码,也介绍了Servlet的作用就是处理请求。但是对于每个请求具体由哪个Servlet处理,却只字未提。其实,每一个URL要交给哪个Servlet处理,具体的映射规则都由一个映射器决定:

这所谓的映射器,其实就是Tomcat中一个叫Mapper的类。

它里面有个internalMapWrapper方法:

定义了7种映射规则:

  • 1.精确匹配 2.前缀匹配

  • 3.扩展名匹配

  • 4.5.6 欢迎列表资源匹配?

  • 7.如果上面都不匹配,则交给DefaultServlet,就是简单地用IO流读取静态资源并响应给浏览器。如果资源找不到,报404错误

简单来说就是:

对于静态资源,Tomcat最后会交由一个叫做DefaultServlet的类来处理对于Servlet ,Tomcat最后会交由一个叫做 InvokerServlet的类来处理对于JSP,Tomcat最后会交由一个叫做JspServlet的类来处理

自定义DispatcherServlet

web.xml

DispatcherServlet

知道了映射器的映射规则后,我们来分析下上图中三种拦截方式会发生什么。

但在此之前,我必须再次强调,我从没说我现在写的是SpringMVC的DispatcherServlet,这是我自己自定义的一个普通Servlet,恰好名字叫DispatcherServlet而已。所以,下面的内容,请当做一个普通Servlet的映射分析。

  • *.do:拦截.do结尾

此时,各个Servlet和谐相处,没问题。

  • /*:拦截所有

拦截localhost:8080

拦截localhost:8080/index.html

拦截localhost:8080/index.jsp

也就是说,/*这种配置,相当于把DefaultServlet、JspServlet以及我们自己写的其他Servlet都“短路”了,它们都失效了。

这会导致两个问题

  • JSP无法被编译成Servlet输出HTML片段(JspServlet短路)
  • HTML/CSS/JS/PNG等资源无法获取(DefaultServlet短路)

  • /:拦截所有,但不包括JSP

拦截localhost:8080

拦截localhost:8080/index.html

不拦截JSP

虽然JSP不拦截了,但是DefaultServlet还是“短路”了。而DispatcherServlet把本属于DefaultServlet的工作也抢过来,却又不会处理(IO读取静态资源返回)。

怎么办?

DispatcherServlet与SpringMVC

SpringMVC的核心控制器叫DispatcherServlet,映射原理和我们上面山寨版的一样,因为本质还是个Servlet。但SpringMVC提供了一个标签,解决上面/无法读取静态资源的问题:

<!-- 静态资源处理  css js imgs -->
<mvc:resources location="/resources/**" mapping="/resources"/>

其他的我也不说了,一张图,大家体会一下DispatcherServlet与SpringMVC到底是什么关系:

DispatcherServlet确实是一个Servlet,但它只是入口,SpringMVC要比想象的庞大。

conf/web.xml与应用的web.xml

conf/web.xml指的是Tomcat全局配置web.xml。

它里面配置了两个Servlet:

也就是JspServlet和DefaultServlet的映射路径。

我们可以按Java中“继承”的思维理解conf/web.xml:

conf/web.xml中的配置相当于写在了每一个应用的web.xml中。

相当于每个应用默认都配置了JSPServlet和DefaultServlet处理JSP和静态资源。

如果我们在应用的web.xml中为DispatcherServlet配置/,会和DefaultServlet产生路径冲突,从而覆盖DefaultServlet。此时,所有对静态资源的请求,映射器都会分发给我们自己写的DispatcherServlet处理。遗憾的是,它只写了业务代码,并不能IO读取并返回静态资源。JspServlet的映射路径没有被覆盖,所以动态资源照常响应。

如果我们在应用的web.xml中为DispatcherServlet配置/*,虽然JspServlet和DefaultServlet拦截路径还是.jsp和/,没有被覆盖,但无奈的是在到达它们之前,请求已经被DispatcherServlet抢去,所以最终不仅无法处理JSP,也无法处理静态资源。

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

进群,大家一起学习,一起进步,一起对抗互联网寒冬

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

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

相关文章

Centos图形化界面封装OpenStack Ubuntu镜像

目录 背景 环境 搭建kvm环境 安装ubuntu虚机 虚机设置 系统安装 登录虚机 安装cloud-init 安装cloud-utils-growpart 关闭实例 删除细节信息 删除网卡细节 使虚机脱离libvirt纳管 结束与验证 压缩与转移 验证是否能够正常运行 背景 一般的镜像文件在上传OpenSt…

计算机毕业设计 基于协同推荐的白酒销售管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

TCA9548A I2C 多路复用器 Arduino 使用相同地址 I2C 设备

在本教程中&#xff0c;我们将学习如何将 TCA9548A I2C 多路复用器与 Arduino 结合使用。我们将讨论如何通过整合硬件解决方案来使用多个具有相同地址的 Arduino 的 I2C 设备。通过使用 TCA9548A I2C 多路复用器&#xff0c;我们将能够增加 Arduino 的 I2C 地址范围&#xff0c…

前端打包添加前缀

vue2添加前缀 router的base加上前缀 export default new Router({mode: history, // 去掉url中的#base: privateDeployUrl, // 这里加上前缀scrollBehavior: () > ({y: 0}),routes: constantRoutes })vue.config.js&#xff0c;publicPath属性加上前缀 publicPath: proces…

组件化编程

hello&#xff0c;我是小索奇&#xff0c;精心制作的Vue系列持续发放&#xff0c;涵盖大量的经验和示例&#xff0c;如果对您有用&#xff0c;可以点赞收藏哈~ 组件化编程 组件是什么&#xff1f; 一句话概括就是&#xff1a;实现特定功能的模块化代码单元 vm就是大哥&#xff…

Leetcode刷题详解——乘积最大子数组

1. 题目链接&#xff1a;152. 乘积最大子数组 2. 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出数组中乘积最大的非空连续子数组&#xff08;该子数组中至少包含一个数字&#xff09;&#xff0c;并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位…

2023-12-03 LeetCode每日一题(可获得的最大点数)

2023-12-03每日一题 一、题目编号 1423. 可获得的最大点数二、题目链接 点击跳转到题目位置 三、题目描述 几张卡牌 排成一行&#xff0c;每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动&#xff0c;你可以从行的开头或者末尾拿一张卡牌&#x…

[HTB][Sherlocks] Meerkat

作为一家快速发展的初创公司&#xff0c;Forela一直在利用商业管理平台。不幸的是&#xff0c;我们的文档很少&#xff0c;而且我们的管理员也不是最有安全意识的。作为我们的新安全提供商&#xff0c;我们希望您查看我们导出的一些PCAP和日志数据&#xff0c;以确认我们是否已…

Nginx实现多虚拟主机配置

Nginx实现多虚拟主机配置 Nginx为什么要进行多虚拟主机配置呢&#xff1f;what&#xff1f; Nginx实现多虚拟主机配置的主要原因是&#xff0c;一个服务器可能会承载多个网站或应用程序&#xff0c;这些网站或应用程序需要使用不同的域名或IP地址来进行访问。如果只有一个虚拟…

代码随想录第二十二天(一刷C语言)|组合总数电话号码的字母组合

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、组合总数 思路&#xff1a;参考carl文档和视频 1、需要一维数组path来存放符合条件的结果&#xff0c;二维数组result来存放结果集。 2、targetSum 目标和&#xff0c;也就是题目中的…

Java基础-----Date类及其相关类(一)

文章目录 1. Date类1.1 简介1.2 构造方法1.3 主要方法 2. DateFormat 类2.1 简介2.2 实例化方式一&#xff1a;通过静态方法的调用2.2 实例化方式二&#xff1a;通过创建子类对象 3. Calendar类4. GregorianCalendar 1. Date类 1.1 简介 java.util.Date:表示指定的时间信息&a…

市面上的AR眼镜:优缺点分析

AR眼镜是近年来备受关注的科技产品之一。它通过将虚拟信息叠加到现实世界中&#xff0c;为用户提供全新的视觉体验。目前&#xff0c;市面上的AR眼镜主要分为两类&#xff1a;消费级AR眼镜和企业级AR眼镜。 消费级AR眼镜 消费级AR眼镜的特点是轻便、时尚、易于佩戴&#xff0…

DOM 事件的注册和移除

前端面试大全DOM 事件的注册和移除 &#x1f31f;经典真题 &#x1f31f;DOM 注册事件 HTML 元素中注册事件 DOM0 级方式注册事件 DOM2 级方式注册事件 &#x1f31f;DOM 移除事件 &#x1f31f;真题解答 &#x1f31f;总结 &#x1f31f;经典真题 总结一下 DOM 中如何…

【STM32】TIM定时器

第一部分&#xff1a;定时器基本定时的功能&#xff1b; 第二部分&#xff1a;定时器的输出比较功能&#xff1b; 第三部分&#xff1a;定时器输入捕获的功能&#xff1b; 第四部分&#xff1a;定时器的编码接口。 1 TIM简介 TIM&#xff08;Timer&#xff09;定时器&#…

计算机网络TCP篇②

一、TCP 重传、滑动窗口、流量控制、拥塞控制 1.1、重传机制 在 TCP 中&#xff0c;当发送端的数据达到接受主机时&#xff0c;接收端主机会返回一个确认应答消息&#xff0c;表示已收到消息。但是在复杂的网络中&#xff0c;并一定能顺利正常的进行数据传输&#xff0c;&…

从零开始搭建博客网站-----登陆页面

登录按钮以及背景图设置 安装element-plus和css插件 npm install element-plus --save npm install sass --save npm install sass-loader --save在main.js里引用 寻找背景图存入assets文件下&#xff0c;并且在Login.vue里设置背景图和登录按钮 设置的背景图的大小没有起…

avue-tabs设置默认选中的tab

文章目录 一、问题二、解决三、最后 一、问题 最近在用avue这个UI框架来开发页面&#xff0c;有用到avue-tabs这个tab切换组件。结果竟然发现element-ui中el-tabs的v-model在avue-tabs中竟然是没有用的&#xff0c;无法设置默认选中哪个tab。avue这个基于element-ui开发的UI框…

〖大前端 - 基础入门三大核心之JS篇㊹〗- DOM事件委托

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

【Erlang进阶学习】2、匿名函数

受到其它一些函数式编程开发语言的影响&#xff0c;在Erlang语言中&#xff0c;将函数作为一个对象&#xff0c;赋予其“变量”的属性&#xff0c;即为我们的匿名函数 或 简称 fun&#xff0c;它具有以下特性&#xff1a; &#xff08;匿名函数&#xff1a;不是定义在Erlang模…

逻辑回归与正则化 逻辑回归、激活函数及其代价函数

逻辑回归、激活函数及其代价函数 线性回归的可行性 对分类算法&#xff0c;其输出结果y只有两种结果{0&#xff0c;1}&#xff0c;分别表示负类和正类&#xff0c;代表没有目标和有目标。 在这种情况下&#xff0c;如果用传统的方法以线性拟合 &#xff08; h θ ( x ) θ T…