JavaWeb 之我终于搞懂了 Servlet

1,325 阅读18分钟

前言


8 月 5 日,晴,今天的太阳依旧是出奇的晒。

今天是学习 JavaWeb 的第三天,学到了很多很多....

tomcat 很好用,用起来很简单,你看,这样解压一下,进入 bin,这样点一下 startup.bat,它就跑起来了

Servlet ,简单,创建一个 .java,继承一下 HttpServlet,重写方法,配置 web.xml 搞定!

So easy

九折?九折?


我承认,我输了,我输得彻彻底底,自从前几天(我也不知道几天了)接触到 Servlet 之后,我脑海中就一直萦绕着十万个为什么

Servlet 或许用起来很简单,它的代码我也都能理解,可我也不知道怎么了,就老是在想,Servlet 到底是个啥,它到底做了什么....

虽然代码都能敲出来,可是感觉就像在云上行走,软绵绵的,不踏实。

终于,在我的不懈努力下,我终于知道 Servlet 是个啥了

本文并没有深入且高大上的 tomcat、Servlet 的源码,对于我这个菜鸡来说也不现实,本文仅是对 tomcat、Servlet 的本质进行的梳理,仅作为个人的学习笔记


前排提示:本文较长,希望阅读本文您能收获到:

  1. web 容器的概念
  2. Tomcat 的基本架构
  3. Tomcat server.xml 的配置
  4. Tomcat 处理静态资源的方式
  5. Servlet 体系结构(Servlet 源码分析)
  6. Servlet 的实际使用

正文

1. Web 服务器 & Web 中间件 & Web 容器

在探究 Servlet 之前,我想需要先了解下 web 容器的概念,以及它和 web 服务器又有什么关系?

  • web 服务器:广义上来说,web 服务器即提供 web 服务的软件或主机,即 web 服务器软件或装有 web 服务器软件的计算机。

    例如:IIS、apache、nginx 等

    Web 服务器可以处理 HTTP 协议,响应针对静态页面或图片的请求,进行页面跳转,或者把动态请求委托其它程序

    所以,我们可以把 IIS、Apache、Nginx、Tomcat 等都称为 Web 服务器,因为他们都提供了 web 服务。

  • 中间件:提供系统软件和应用软件之间连接的软件,以便于软件各部件之间的沟通

    中间件处在操作系统和更高一级应用程序之间。

    它充当的功能是:将应用程序运行环境与操作系统隔离,从而实现应用程序开发者不必为更多系统问题忧虑,而直接关注该应用程序在解决问题的能力

    简而言之:在操作系统与我们所编写的应用程序之间,应该存在一种中间介质,可以让我们更多的关注与应用程序本身的业务逻辑,而不用关心与操作系统交互,更甚者是底层的东西。更倾向于 Api 的使用

  • 容器:中间件的一种,作为操作系统和应用程序之间的桥梁

    给处于其中的应用程序组件提供一个环境,使应用程序直接跟容器中的环境变量交互,而不必关注其他系统问题。

  • web 容器:我们把遵守 JavaEE 规范标准的 web 服务器就叫做 JavaEE 中的 web 容器。处理 web 服务的容器

    例如:tomcat(Servlet 容器)

    web 容器用于给处于其中的应用程序组件(ASP、JSP)提供一个环境,是中间件的一个组成部分,它实现了对动态语言的解析。比如 Tomcat 可以解析 JSP,是因为其内部有一个 Servlet 容器

简而言之

web 服务器:能够为外界提供服务(静态资源、动态资源)

中间件:帮助应用程序与系统交互的软件

web 容器:能够帮助应用程序(我们所编写的 JavaWeb 程序)与系统交互、且遵守 JavaEE 规范标准的中间件

三者的关系:web 服务器 > web 中间件 > web 容器

2. Tomcat

Tomcat 服务器是一个开源的轻量级 Web 应用服务器,在中小型系统和并发量小的场合下被普遍使用,是开发和调试 Servlet、JSP 程序的首选。

我想,这是大多数网上的关于 tomcat 的文章的第一句,Me too 😂

我也是抄来的

服务器的作用在这里不再赘述,我们直接上干货,Tomcat 的架构

2.1 Tomcat 原理

  • 首先,我们看下真实存在的:Tomcat 目录

  • 然后是,看不见摸不着,存在于逻辑上的:Tomcat 结构图

在看 Tomcat 结构图的时候,建议打开你的 Tomcat 目录,找到 conf 下的 server.xml 配置文件,你就会发现新大陆

  • 一个 server.xml 配置文件如下展示:


🐅 Tomcat 主要组件:服务器 Server、服务 Service、连接器 Connector

连接器 Connector 和容器 Container 是 Tomcat 的核心.

一个 Container 容器和一个或多个 Connector 组合在一起,加上其他一些支持的组件共同组成一个 Service 服务,有了 Service 服务便可以对外提供能力了,但是 Service 服务的生存需要一个环境,这个环境便是 Server,Server 组件为 Service 服务的正常使用提供了生存环境,Server 组件可以同时管理一个或多个 Service 服务。

Tomcat 两大组件

  • Connector


    📌 一个 Connector 将在某个指定的端口上侦听客户请求,接收浏览器发过来的 TCP 连接请求(面向连接),创建一个 Request 和 Response 对象,用于和请求端交换数据。

    📌 然后会产生一个线程来处理这个请求,并把产生的 RequestResponse 对象传给处理 Engine(Container 中的一部分),从 Engine 处获得响应,并返回给请求端。

    📌 Tomcat 中有两个经典的 Connector

    一个直接侦听来自 Browser 的 HTTP 请求; 另外一个来自其他的 WebServer 请求。

    HTTP/1.1 Connector 在端口 8080 处侦听来自客户 Browser 的 HTTP 请求,AJP/1.3 Connector 在端口 8009 处侦听其他 Web Server(其他的 HTTP 服务器)的 Servlet/JSP 请求。


    Connector 最重要的功能就是接收连接请求然后分配线程让 Container 来处理这个请求,所以这必然是多线程的,多线程的处理是 Connector 设计的核心。

  • Container


📌 Container 是容器的父接口,该容器的设计用的是典型的责任链的设计模式,它由四个子容器组件构成,分别是 **Engine、Host、Context、Wrapper;**这四个组件是负责关系,存在包含关系。

📌 通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 定义多个 Wrapper,多个 Wrapper 就要定义一个更高的 Container,如 Context

📌 Context 可以定义在父容器 Host 中,Host 不是必须的,但是要运行 war 程序,就必须要 Host,因为 war 中必有 web.xml 文件,这个文件的解析就需要 Host 了,如果有多个 Host 就要定义一个 top 容器 Engine 了,而 Engine 没有父容器了,所以一个 Engine 就代表了一个完整的 Servlet 引擎


  • Engine 容器

    Engine 容器比较简单,它只定义了一些基本的关联关系

  • Host 容器

    Host 是 Engine 的子容器,一个 Host 在 Engine 中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们。它的子容器通常是 Context,它除了关联子容器外,还有就是保存一个主机应该有的信息。

  • Context 容器

    Context 代表 Servlet 的 Context,它具备了 Servlet 运行的基本环境,理论上只要有 Context 就能运行 Servlet 了。简单的 Tomcat 可以没有 Engine 和 Host。Context 最重要的功能就是管理它里面的 Servlet 实例,Servlet 实例在 Context 中是以 Wrapper 出现的,还有一点就是 Context 如何才能找到正确的 Servlet 来执行它呢? Tomcat5 以前是通过一个 Mapper 类来管理的,Tomcat5 以后这个功能被移到了 request 中,在前面的时序图中就可以发现获取子容器都是通过 request 来分配的。

  • Wrapper 容器

    Wrapper 代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。

    Wrapper 的实现类是 StandardWrapper,StandardWrapper 还实现了拥有一个 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 将直接和 Servlet 的各种信息打交道。

其他组件

Tomcat 还有其它重要的组件,如安全组件 security、logger 日志组件、session、mbeans、naming 等其它组件。这些组件共同为 Connector 和 Container 提供必要的服务。

2.2 配置 Tomcat server.xml

在前文中,我们已经大致了解了 Tomcat 的架构,下面我们根据 server.xml 来感受一下 tomcat 的结构划分


xml 文件中的配置结构和 Tomcat 的架构是一一对应的

  • 根目录是 <Server>,代表服务器,<Server> 下面有一个 <Service>,代表服务。

    Service 包装 Executor、Connector、Engine,以组成一个完整的服务

  • <Service> 下有两个 <Connector>,代表连接(需要的话可以再加)

    Connector 是 Tomcat 接收请求的入口,每个 Connector 有自己专属的监听端口

    • HTTP/1.1 Connector 在端口 8080 处侦听来自客户 Browser 的 HTTP 请求。
    • AJP/1.3 Connector 在端口 8009 处侦听其他 Web Server(其他的 HTTP 服务器)的 Servlet/JSP 请求。
  • 与 Connector 平级的还有个 <Engine> (也就是上文中的 Servlet 引擎)

    Engine 负责处理 Service 内的所有请求。

    它接收来自 Connector 的请求,并决定传给哪个 Host 来处理,Host 处理完请求后,将结果返回给 Engine,Engine 再将结果返回给 Connector

  • Engine 下面有个 <Host>,代表主机

    Host 负责管理一个或多个 Web 项目

  • <Context> 代表一个运行在 Host 上的 Web 项目

    一个 Host 上可以有多个 Context。将一个 Web 项目(D:\MyApp)添加到 Tomcat,在 Host 标签内,添加 Context 标签: <Context path="" docBase="D:\MyApp" reloadable="true" crossContext="true">

以上,仅是对于 server.xml 配置的一个简略概览,详细的配置请参考:server.xml 配置


server.xml 案例

<?xml version="1.0" encoding="UTF-8"?>
<!--
     Server.xml文件中的配置结构和Tomcat的架构是一一对应的。
     根目录是<Server>,代表服务器
-->
<Server port="8005" shutdown="SHUTDOWN">
    <!-- Listener 即监听器,负责监听特定的事件 -->
    <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
    <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>

    <!-- GlobalNamingResources 用于配置 JNDI -->
    <GlobalNamingResources>
        <Resource name="UserDatabase"
                  auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml"/>
    </GlobalNamingResources>

    <!--
        <Server>下面有且仅有1个<Service>,代表服务。
        Service包装Executor、Connector、Engine,以组成一个完整的服务
    -->
    <Service name="Catalina">
        <!--
            Executor 即 Service 提供的线程池,供Service内各组件使用
        -->
        <!--<Executor name="Super无敌黑旋风"
                  namePrefix="super无敌黑旋风"/>-->

        <!--
            Connector是Tomcat接收请求的入口,每个Connector有自己专属的监听端口
            Connector有两种:HTTP Connector 和 AJP Connector

            Tomcat默认配置了两个端口:
             一个是HTTP/1.1协议的 === 专门处理HTTP请求。
             一个是AJP/1.3协议 === 侦听其他Web Server(其他的HTTP服务器)的Servlet/JSP请求。

             Connector 负责监听端口,将请求转给 Engine
        -->

        <!-- 专门处理 HTTP 请求 -->
        <Connector port="8080"
                   protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443"/>

        <!-- 侦听其他Web Server(其他的HTTP服务器)的Servlet/JSP请求 -->
        <Connector port="8009"
                   protocol="AJP/1.3"
                   redirectPort="8443"/>

        <!--
             Engine 负责处理 Service 内的所有请求。
             它接收来自 Connector 的请求,并决定传给哪个 Host 来处理,
             Host 处理完请求后,将结果返回给 Engine,
             Engine 再将结果返回给 Connector.
        -->
        <Engine name="Catalina" defaultHost="localhost">
            <Realm className="org.apache.catalina.realm.LockOutRealm">
                <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                       resourceName="UserDatabase"/>
            </Realm>

            <!-- Host 负责管理一个或多个 Web 项目 -->
            <Host name="localhost"
                  appBase="webapps"
                  unpackWARs="true"
                  autoDeploy="true">

                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                       prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b"/>
                <!--
                    Context 代表一个运行在 Host 上的 Web 项目

                    配置虚拟目录
                        path:访问资源 URI。URI 名称可以随便起,但是必须在前面加上一个/
                        docBase:资源所在的磁盘物理地址

                    推荐在 conf\Catalina\localhost 目录下以 xml 文件的方式配置虚拟目录
                        对 tomcat 根配置文件的侵入性更小,想要去掉配置的虚拟目录,删掉配置文件即可
                -->
                <!--<Context path="/ttt" docBase="test"/>-->
            </Host>

            <!-- 配置虚拟主机 -->
            <!--<Host name="www.myweb.cn"
                  appBase="f:/myweb"
                  unpackWARs="true"
                  autoDeploy="true">

                <Context path="/" docBase="web"/>
            </Host>-->
        </Engine>
    </Service>
</Server>

2.3 一个 Http 请求流程


Tomcat Server 处理一个 HTTP 请求的过程

  1. 客户端发送请求(刷新页面、点击按钮),请求被发送到本机端口 8080,被在那里监听的 Coyote HTTP/1.1 Connector 获得;

  2. Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待 Engine 的回应;

  3. Engine 获得请求 localhost/test/index.jsp,匹配所有的虚拟主机 Host;

  4. Engine 匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机);

  5. 名为 localhost 的 Host 获得请求 /test/index.jsp,匹配它所拥有的所有的 Context.

  6. Host 匹配到路径为 /test 的 Context(如果匹配不到,就把该请求交给路径名为 "" 的 Context 去处理);

  7. path="/test" 的 Context 获取的请求 "/index.jsp",在它的 mapping table 中寻找出对应的 Servlet。

  8. Context 匹配到 <url-pattern>*.jsp</url-pattern> 的 Servlet,对应于 JspServlet 类

    • 能够处理 jsp 的 Servlet(tomcat/conf/web.xml):
  • 该 Servlet 的映射(tomcat/conf/web.xml):
  1. 构造 HttpServletRequest 对象和 HttpSerletReasponse 对象,作为参数,调用 JspServlet 的 doGet() 或 doPost() 执行业务逻辑、数据存储等程序;
  2. Context 把执行完之后的 HttpServletResponse 对象返回给 Host;
  3. Host 把 HttpServletResponse 对象返回个 Engine;
  4. Engine 把 HttpServletResponse 对象返回给 Connector;
  5. Connector 把 HttpServletResponse 对象返回给客户端 Browser。

2.4 Tomcat 处理静态资源

Tomcat 处理静态资源可以归为一句话:

Tomcat 中的请求都是由 Servlet 处理,静态资源也不例外。


可能我们习惯性的打开 Tomcat,部署了一个应用之后,就开始查看其运行结果,或者更多的关注 Servlet 内的逻辑是否正确。至于静态资源处理这个事,根本没有留意过,

本质上讲,Tomcat 对于所有的静态资源,会做同一处理。也就是在所有你没有配置 URL 匹配的地方(没有对应的 Servlet),Tomcat 这个全局同一处理的配置就开始接管工作了。

关于这个全局同一处理的配置,它就在 Tomcat 的 conf 目录下,有一个 web.xml 打开它之后,你可以全局搜索:DefaultServlet,全局就只有一个,如下:

怎么样,看到这里是不是很眼熟,这就是一个 Servlet 的声明,这个 Servlet 声明所关联的 Servlet class 也就是 org.apache.catalina.servlets.DefaultServlet 这个类

此外,它还有一个名为 listings 的初始化参数,默认值为 false。

这个参数主要作用,是在没有 welcome 文件时,控制应用目录内的文件是否允许列表显示。如果设置为 true,就会常见的 FTP 服务器一样,把应用目录下的文件都列了出来,像下面这个样子。当然,这个样式也是可以自已定义的。

再往下看:

default Servlet 的映射的 url 为 /

你会不会有疑惑,既然 url-pattern 配置的是 / ,那不就应该响应所有的请求了么?

对,我们上面说明中也提到了,是匹配所有你没定义Servlet-mapping 的请求。

而之所以自己定义的 Servlet 可以优先生效,则是因为 Tomcat 内的 Servlet 配置,是严格按照声明顺序初始化,并按此顺序响应请求,一层层按此比对,有一个可以响应请求,就用其处理。

如有兴趣可自行查看 org.apache.catalina.servlets.DefaultServlet 源码

3. Servlet

好,今天的主角驾到了

3.1 Servlet 调用

先别急,最近,你有没有发现:我们已经很久没有写 main 方法了,我想后续应该也是如此。

我们已经不再关心、甚至根本不知道到底谁调用了我写的这个程序,反正我写了一个类,甚至从来没 new 过,它就跑起来了...

其实,这一切的一切,简单来说就是 “注入” 和 “回调”。

Tomcat 中有个 mian 方法,假设是这样的:

反正大概的意思,我们所写的 Servlet class 在 tomcat 的一顿操作下,得到调用,能够执行我们期望的逻辑

当读到了这里的时候,是否在不知不觉中已经理解了 Servlet 是什么了呢

3.2 Tomcat 里全是 Servlet

我们已知:Tomcat 中的请求都是由 Servlet 处理

  • 静态资源是由 DefaultServlet 处理
  • Jsp 是由 JspServlet 处理
  • 还有其他我们自己定义的 Servlet

Servlet 的划分大致如下:

3.3 Servlet 究竟是什么

Servlet 的作用与位置

在我们开发 JavaWeb 的时候,总是需要用到 web 服务器来部署我们的项目,无论是在开发中,还是生产环境中。

我们就以轻量级的 tomcat 这个 “服务器” 来说,实际上

tomcat 服务器 = web 服务器 + web 容器

tomcat 接收一个请求的过程如下:


Connector 组件监听必要的端口(8080),当获取到请求后,Connector 组件会创建两个对象:

  • request:截取请求中的各种信息,封装到对象中
  • response:空的对象

然后会将这个两个对象传递给 Container 组件,Container 是 tomcat 中的另一个核心组件

Container 内又分为 Engine > Host > Context > Wrapper(Servlet)

Container 拿到请求,将两个对象逐层传递,最终到达 Servlet 处,由 Servlet 完成对请求的处理(逻辑处理)

当 Servlet 处理完毕之后,会将处理结果放到 response 中,然后逐上层传递,最终到达 Connector 手中,返回给客户端那里


由以上所有可知:

  • Servlet 位于 Tomcat 架构中的最后一层
  • Servlet 用来处理请求,并将处理的结果返回

所以 Servlet 究竟是什么?

这个还要从 JavaEE 说起,JavaEE 其实就是基于 JAVA 技术的一系列标准。

上世纪 90 年代在大洋彼岸,有一家名唤 SUN 的公司,创造了一门全新的语言,叫 Java。

经过短短几年的发展,一跃成为市场上最炙手可热的语言。

随后又悟出“Java13 绝技”,也就是所谓的 JavaEE 规范:

JDBC,JNDI,EJB,RMI,JSP,Servlets,XML,JMS,Java IDL,JTS,JTA,JavaMail,JAF。

看到没,Servlet 就是一个 JavaEE 中的规范

可以这么解答:

Serlvet 是 Java 中的一种技术,JavaEE 定义了这种技术的规范,也仅是一种规范

规范的目的是什么呢,是不是来实现功能,于是就有人根据这个规范生产出来产品,我们要使用别人的产品,就要按照规范(说明说)来操作,才能够发挥这个产品的作用

而这个产品的作用就是处理请求,返回结果

而这个 “产品” 其实就是 Servlet 容器

而这个 Servlet 容器,在 Tomcat 中就有,当然,还有其他的地方(服务器软件)存在 Servlet 容器

总结何为 Servlet

  • Servlet 是 JavaEE 中的一个规范
  • 只有规范是不行的,要有具体的实现,也就是 Servlet 产品
  • 我们可以通过我们熟悉的 Servlet 规范,来使用 Servlet 产品
  • 通过使用 Servlet 产品,才能够实现对于请求的处理
  • 而 Servlet 产品在各个 web 服务器中就是所谓的 web 容器(Servlet 容器)

4. 使用 Servlet

好,经历了全文的铺垫,终于要见到 Servlet 了,写了这么多,如果你能够通篇读完,能够融会贯通,我可以告诉你,Servlet 就是个 💨💨💨

让我们来揭开 Servlet 的遮羞布

4.1 interface Servlet

Servlet 在代码中,其实就是一个 接口 -- interface

So so

对,你没有看错,这个接口里面就只有 5 个方法

五个方法,最难的地方在于形参,然而 Tomcat 会事先把形参对象封装好传递给我们,除此之外,即不要我们写 TCP 连接数据库,也不需要我们解析 HTTP 请求,更不需要我把结果转成 HTTP 响应,request 对象和 response 对象帮我搞定了

还记的在 Connector 获取到请求的时候创建的两个对象吗,就是 request 和 response,然后一层层传递下来的,对吧?

这样一来,Servlet 的实现类,实际上只是空壳,你只要重写它的方法,在里面写上逻辑代码,tomcat 就自己执行了.

So easy

4.2 Servlet 接口中的三个参数

  • ServletConfig:顾名思义,“Servlet 配置”

    回想一下,我们在哪里配置了 Servlet 吗?是不是在 web.xml 中

    其实,这个 ServletConfig 就是 Tomcat 将我们写在 web.xml 中的配置项,解析了出来,然后封装成一个 Servlet 对象,传递给我们

  • Request/Response

    简单,请求对象/响应对象

    我们早早在上面强调了这两个对象的创建时机,及方式,这里就不再赘述

4.3 GenericServlet

当我们将 Servlet 的遮羞布扯开后,是如此的简单

可它是一个接口啊,如果我们每次都是通过实现 interface Servlet 的方式创建 Servlet,那我们每次都要重写里面所有的方法,或许这并不是我们想要的,我们也许每次只是想要使用一下 service 方法,进行简单的处理请求就可以了

那么,通过实现 interface Servlet 的方式,是不是太过冗余了呢

不要着急,Java 还给我们提供了另一个方式,通过继承 GenericServlet 来创建一个 Servlet

看下我们的实现代码:

package com.itheima.servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class ExtendsGenericServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }
}

是不是我们只重写了一个 service 方法,就可以了,事情变得如此简单

那我们再来看下这个 GenericServlet 是何方神圣

可以看到是一个 实现了 Servlet 接口的 抽象类

我们发现 GenericServlet 做了一下改良:

  • 提升了 init 方法中原本是形参的 ServletConfig 对象的作用域(局部变量 -> 成员变量),方便其他方法使用
  • init 方法中还调用了一个 init 空参构造,如果我们希望在 Servlet 创建是做一些什么初始化操作,可以继承 GenericServlet 后,覆盖 init 空参构造
  • 由于其他方法内也可以使用 ServletConfig ,于是写了一个 getServletContext 方法

于是,GenericServlet 简化了我们的创建流程(仅需重写 service 方法)

但是,由于请求的方式又分好多种,我们在实际通过 service 方法处理请求的时候,往往还要进行请求方式的判断(比如:判断当前请求是 get/post/put/delete...)

这样对于我们来说,又出现了大堆冗余的代码

别急,还有...

4.4 HttpServlet

HttpServlet 抽象类

HttpServlet 结构图


可以看到 HttpServlet 继承了 GenericServlet

查看源码,可以看到 HttpServlet 已经实现了 service 方法

简而言之:HttpServlet 的 service 方法已经替我们完成了复杂的请求判断

好了,这下更简单了,我们只需要在 HttpServlet 的派生类中 重写 doGet/doPost/doPut... 这些方法就可以了

就像这样:

public class ExtendsHttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // ......

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // .....
    }
}

OK,Servlet 如此简单

九折?九折?九折?九折?九折?九折?九折?九折?九折?

参考于

鸣谢 侵删

web 服务器、Web 中间件和 Web 容器的区别

Tomcat 外传

Tomcat(一):简介

Tomcat(二):server.xml 配置

Tomcat 是如何响应静态资源的?

Tomcat 对静态资源的处理

tomcat 中对静态资源的访问也会用 servlet 来处理吗?

servlet 的本质是什么,它是如何工作的?