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

乎语百科 268 0

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水一篇。哈哈。

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

标签:

留言评论

  • 这篇文章还没有收到评论,赶紧来抢沙发吧~