Spring mvc源码分析系列--Servlet的前世今生

概述

上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑。今天暂且来填一个小坑,这篇文章我们来说说Servlet的发展历史。所以这篇文章还是比较轻松,不涉及太多的源码分析,简单介绍Servlet的由来和发展。

Servlet是什么

传说在上世纪90年代,因为nternet和浏览器的飞速发展,使得基于浏览器的B/S模式随之火爆发展起来。最初,用户使用浏览器向WEB服务器发送的请求都是请求静态的资源,比如html、css等。 但是可以想象:根据用户请求的不同动态的处理并返回资源是理所当然必须的要求,例如用户提交一些东西,服务器就能按提交的内容反馈用户不同的效果。所以人们应该非常迫切想要推出一项技术来实现动态的处理, java 为了应对上述需求,促进了servlet技术诞生。

Servlet 是在服务器上运行的小程序。这个词是在 Java applet的环境中创造的,Java applet 是一种当作单独文件跟网页一起发送的小程序,它通常用于在客户端运行,结果得到为用户进行运算或者根据用户互作用定位图形等服务。服务器上需要一些程序,常常是根据用户输入访问数据库的程序。这些通常是使用公共网关接口(Common Gateway Interface,CGI)应用程序完成的。然而,在服务器上运行 Java,这种程序可使用 Java 编程语言实现。在通信量大的服务器上,JavaServlet 的优点在于它们的执行速度更快于 CGI 程序。各个用户请求被激活成单个程序中的一个线程,而无需创建单独的进程,这意味着服务器端处理请求的系统开销将明显降低。不清楚CGI是什么?这篇文章CGI是什么可以解答你的疑问。

Servlet与 CGI 比较存在的优点如下:

  • 与传统的 CGI 和许多其他类似 CGI 的技术相比,Java Servlet 具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资。在未来的技术发展过程中,Servlet 有可能彻底取代 CGI。

  • 在传统的 CGI中,每个请求都要启动一个新的进程,如果 CGI 程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在 Servlet 中,每个请求由一个轻量级的 Java 线程处理(而不是重量级的操作系统进程)。

  • 在传统 CGI 中,如果有 N 个并发的对同一 CGI程序的请求,则该CGI程序的代码在内存中重复装载了 N 次;而对于 Servlet,处理请求的是 N 个线程,只需要一份 Servlet 类代码。在性能优化方面,Servlet 也比 CGI 有着更多的选择。

Servlet可以说是Java技术中最早的Web解决方案,Servlet与普通Java类的编写非常类似。在Servlet中可以通过挨着行输出Html等语句来实现页面的样式和输出,数据的动态功能当然也就实现了。表现、逻辑、控制、业务全部混在Servlet类中。下面给出一个简单例子来直观感受一下。

public void doGet(HttpServletRequest request,HttpServletResponse)
throws IOException,ServletException
{
response.setContentType("text/html;charset=gb2312");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>Hello World!</title></head>");
out.println("<body>");
out.println("<p>Hello World!</p>");
out.println("</body></html>");
}

Servlet是怎么运行的

上一小节介绍到,Servlet是用于处理动态响应客户端请求的。那么Servlet是运行在哪里的呢?

最早支持 Servlet 技术的是 JavaSoft 的 Java Web Server。此后,一些其它的基于 Java 的 Web Server 开始支持标准的 Servlet API。Servlet 的主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。

还记得上一篇文章里的灵魂拷问吗? 浏览器的一个请求,是如何精确到达你的web服务器里的业务逻辑里的,其中经历的流程能说个所以然吗 ,这个过程为:

  1. 客户端发送请求至服务器端。
  2. 服务器将请求信息发送至 Servlet。
  3. Servlet 生成响应内容并将其传给服务器。响应内容动态生成,通常取决于客户端的请求。
  4. 服务器将响应返回给客户端。

以上的每一步都包含着大量的细节,现在广泛使用的web服务器是Tomcat,以Tomcat为例,简单分析一下以上的四步:

  • 客户端发送请求至服务器端。这部分涉及的是计算机网络的基础知识,主要涉及各种协议,例如:ARP、DNS、TCP,HTTP等。
  • 服务器将请求信息发送至 Servlet。这里就涉及的是具体的web服务器实现了,以Tomcat为例,这里不展开细说,请求到达Tomcat后,会经过各种阀门的处理,然后最终进入到我们的Servlet里面,这里附上Tomcat的整体处理流程图。

  • Servlet 生成响应内容并将其传给服务器。这部分没啥好说,就是具体的业务逻辑。
  • 服务器将响应返回给客户端。跟第一点类似。

Servlet与Tomcat的关系

Tomcat是一个web服务器,又有人称其为Servlet容器,那么顾名思义,Tomcat运行时会包含很多的Servlet在其中,当请求到达Tomcat时,Tomcat会帮我们将请求封装成一个Request对象,经过不同层级的阀门处理后,转发到了具体的Servlet里。

所以可以看到二者的关系为:Servlet的运行依赖于Tomcat,Tomcat会为其提供很多基础功能的支持。同时Tomcat对请求的业务处理是由具体的Servlet去实现,二者的结合有条不紊,实现了一个完整的web服务器功能。

我们来看一下Servlet的发展历史,可以看到Servlet的第一个版本发布在1997年。

版本 日期 JAVA EE/JDK版本 特性
Servlet 4.0 2017年10月 JavaEE 8 HTTP2 [1]
Servlet 3.1 2013年5月 JavaEE 7 Non-blocking I/O, HTTP protocol upgrade mechanism
Servlet 3.0 2009年12月 JavaEE 6, JavaSE 6 Pluggability, Ease of development, Async Servlet, Security, File Uploading
Servlet 2.5 2005年10月 JavaEE 5, JavaSE 5 Requires JavaSE 5, supports annotation
Servlet 2.4 2003年11月 J2EE 1.4, J2SE 1.3 web.xml uses XML Schema
Servlet 2.3 2001年8月 J2EE 1.3, J2SE 1.2 Addition of Filter
Servlet 2.2 1999年8月 J2EE 1.2, J2SE 1.2 Becomes part of J2EE, introduced independent web applications in .war files
Servlet 2.1 1998年11月 未指定 First official specification, added RequestDispatcher, ServletContext
Servlet 2.0 JDK 1.1 Part of Java Servlet Development Kit 2.0
Servlet 1.0 1997年6月

再看Tomcat的发展历史,可以看到Tomcat的第一个版本是晚于Servlet的,所以Tomcat也被认为是最早比较完善的对Servlet支持的web服务器。

版本 日期 JAVA EE/JDK版本
tomcat-10 2021-06-16 JDK 11
tomcat-9 2015-11-19 JDK 1.8
tomcat-8 2013-08-05 JDK 1.7
tomcat-7 2010-06-13 JDK 1.6
tomcat-6 2006-10-21 JDK 1.5
tomcat-5 2004-08-29 JDK 1.4
tomcat-4 2003-09-06 JDK 1.3
tomcat-3 2003-09-06 JDK 1.1

再论Servlet是什么

打开代码,可以看到Servlet其实是一个接口,接口意味着什么?意味着是规范,任何对它的合理实现都可以认为是一个Servlet,以我们常用的http为例,对http的支持是HttpServlet,看一下它的类继承图,可以看到它就是实现了Servlet接口。

简单看一下Servlet接口定义的方法,可以看到只有五个方法,包含了初始化,执行业务逻辑,销毁等重要过程。

其中重点的是service()方法。那么这个方法是在哪里被执行了呢?上面我们说过,Servlet是依赖于Tomcat运行的,所以这个方法应该是在Tomcat里被调用了,我们看一下代码。

发现service()方法会在org.apache.catalina.core.ApplicationFilterChain#internalDoFilter(ServletRequest request, ServletResponse response)里被调用。看到这里,大家应该清楚Servlet如何跟Tomcat串联起来了吧。

至于我们写的Servlet是怎么塞到了ApplicationFilterChain里面,可以去看后续系列Tomcat的原理分析(又在挖坑,我直接好家伙)。

小试牛刀

前面说了辣么多,那Servlet项目是什么结构,以及如何运行的,下面我们返璞归真搞个简单的Servlet项目来试试看。

新建一个项目,勾上。

过程省略,可参考文章,最终项目结构如下。

MyServlet代码如下。

/**
* @author Codegitz
* @date 2022/9/28
**/
@WebServlet({"/myServlet"})
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("invoke MyServlet#doGet() method");
doPost(req,resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("invoke MyServlet#doPost() method");
resp.getWriter().write("<h1>Hello World</h1>");
}
}

启动Tomcat就可以访问了。麻雀虽小五脏俱全,这就是一个简单的Servlet项目构建过程。可以看到这个纯粹的Servlet项目,没有涉及到Spring mvc的东西,那么如何涉及到Spring mvc后,项目会变成什么样呢?这个我们下一篇文章会介绍。

总结

这篇文章简单介绍了一下Servlet的发展历史,然后顺带简单介绍了Tomcat的主要版本已经他们之间的关系。最后是简单实现了一个Servlet,这里还没真正涉及到Spring mvc的内容。

下一篇就会真正的开始Spring mvc的分析,会简单介绍一下mvc的发展历史,随后通过一个小demo引入,然后开始源码分析。

这篇文章太简单了,you水一篇。哈哈。

如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。

Spring mvc源码分析系列--Servlet的前世今生的更多相关文章

  1. Spring mvc源码分析系列--前言

    Spring mvc源码分析系列--前言 前言 距离上次写文章已经过去接近两个月了,Spring mvc系列其实一直都想写,但是却不知道如何下笔,原因有如下几点: 现在项目开发前后端分离的趋势不可阻挡 ...

  2. 精尽Spring MVC源码分析 - 寻找遗失的 web.xml

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. 精尽Spring MVC源码分析 - 一个请求的旅行过程

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 精尽Spring MVC源码分析 - MultipartResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  6. 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  7. 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. 精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  9. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

随机推荐

  1. 实践GoF的23种设计模式:观察者模式

    摘要:当你需要监听某个状态的变更,且在状态变更时通知到监听者,用观察者模式吧. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:观察者模式>,作者: 元闰子 . 简介 现在有 ...

  2. 关于javascript中this

    ------------恢复内容开始------------ 1 var number = 5; 2 var obj = { 3 number: 3, 4 fn1: (function () { 5 ...

  3. 6.14 YZBOI模拟赛solution

    \(6.14\ YZBOI\)模拟赛\(solution\) 本来不想写题解来着...毕竟是自己找的题还是写一写吧 上午为了整活,就把赛制改成\(IOI\)赛制了,于是乎拯救了大家的\(70pts\) ...

  4. MODBUS转PROFINET网关将电力智能监控仪表接入PROFINET网络案例

    本案例控制的主要对象是变送器的显示与报警.系统主PLC 选用西门子CPU,通过小疆智控MODBUS 转 PROFINET网关GW-PN5001采集IM300电力智能监控仪数据. 1.首先加入 GSD ...

  5. 浅淡 Apache Kylin 与 ClickHouse 的对比

    作者简介 周耀,Kyligence 解决方案架构师,Apache Kylin.Apache Superset Contributor. Apache Kylin 和 ClickHouse 都是目前市场 ...

  6. 移动端实现HTML5 mp3录音踩坑指南:系统播放音量变小、一些机型录音断断续续 之 MediaRecorder和AudioWorklet的终极对决

    目录 H5录音见坑填坑 采用MediaRecorder采集音频 音频格式:WebM和PCM 从WebM封装容器中提取PCM数据 录音的兼容性 困扰已久的H5录音时系统播放音量变小的问题 H5录音见坑填 ...

  7. Vue组件的继承用法

    Vue组件的继承用法 点击打开视频讲解 vue组件的继承适用于UI几乎一样,只是数据不一样的情况下,但是这种情况通过统一封装组件也是能实现的,小功能建议用封装,大功能建议用组件继承,因为大功能在结合搜 ...

  8. CSS 布局(一):Flex 布局

    主轴和纵轴 对某一盒子模型添加display: flex;属性后,称之为 Flexible Box 模型(或称之为 flexbox),即弹性盒子模型,简称弹性盒子. 弹性盒子为子元素提供了强大的空间分 ...

  9. linux项目环境部署入门

    linux目录 /bin 二进制可执行命令 (ls,cat,mkdir等) /dev 设备特殊文件/etc 系统管理和配置文件/etc/rc.d 启动的配置文件和脚本 /opt 额外安装的可选应用程序 ...

  10. PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏

    一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...