Spring mvc源码分析系列--Servlet的前世今生
概述
上一篇文章Spring mvc源码分析系列--前言挖了坑,但是由于最近需求繁忙,一直没有时间填坑。今天暂且来填一个小坑,这篇文章我们来说说Servlet的发展历史。所以这篇文章还是比较轻松,不涉及太多的源码分析,简单介绍Servlet的由来和发展。
Servlet是什么
传说在上世纪90年代,因为Internet和浏览器的飞速发展,使得基于浏览器的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服务器里的业务逻辑里的,其中经历的流程能说个所以然吗 ,这个过程为:
- 客户端发送请求至服务器端。
- 服务器将请求信息发送至 Servlet。
- Servlet 生成响应内容并将其传给服务器。响应内容动态生成,通常取决于客户端的请求。
- 服务器将响应返回给客户端。
以上的每一步都包含着大量的细节,现在广泛使用的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水一篇。哈哈。
如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。