戴老师的博客 - servlet
type: channel
link: https://tushu.info/tag/servlet/
items: Servlet总结
items: Tomcat中Servlet的创建初始化体系结构及工作
items: Servlet 工作原理解析
items: Servlet工作原理解析
items: Servlet 3.0 新特性详解（servlet是单实例多线程的，线程池数量有限）
items: Spring MVC 3.2 技术预览（一）：Servlet 3介绍，异步支持

Servlet总结
type: item
date: 2026-07-07T08:36:19+00:00
description: Servlet 总结什么是 Servlet？Servlet 是 Java EE 规范中的核心组件，它是一种运行在服务器端的 Java 程序，主要用于处理客户端请求并作出响应。Servlet 的执行过程？请求发送：客户端发送请求到服务器端。请求转发：服务器端根据 web.xml 文件中的 Servlet 相关配置信息，将客户端请求转发到相应的 Servlet。业务处理：Servlet 根据 request 对象中封装的用户请求与数据库进行交互；返回数据之后，Servlet 将数据封装到 response 对象中。响应返回：控制权从 Servlet 重新回到服务器端，服务器端将响应信息返回给客户端，并跳转到相应的页面。Servlet 的生命周期？加载和实例化：在第一次请求 Servlet 时，Servlet 容器将会创建 Servlet 实例。初始化：Servlet 容器加载完成 Servlet 之后，必须进行初始化，此时 init() 方法将被调用。服务请求：Servlet 初始化之后，处于响应请求的就绪状态。此时如有客户端请求发送，就会调用 Servlet 实例的 service() 方法，并且根据用户的请求方式，调用 doPost() 或者 doGet() 方法。销毁：最后，Servlet 容器负责将 Servlet 实例进行销毁，调用 destroy() 方法实现。Servlet 与 JSP 的关系及区别？关系：Servlet 是 JSP 的基础，JSP 是 Servlet 技术的扩展。JSP 运行之前首先会被编译为一个 Servlet。区别：JSP 侧重于视图（View）展示；Servlet 主要用于控制业务逻辑（Controller）。Servlet API 中的 forward() 与 redirect() 的区别？特性请求转发 (forward.html)重定向 (redirect.html)实现方法Request 对象的方法Response 对象的方法执行位置服务器端执行客户端浏览器执行请求次数1 次请求2 次请求地址栏变化不会改变发生改变数据共享始终在同一个 Request 域中，可共享资源第二次请求将丢失第一次 Request 中的资源性能优于重定向相对较低Servlet 如何同时处理多个请求？Servlet 采用多线程来处理多个请求的同时访问。Servlet 容器通过线程池来管理维护服务请求。所谓线程池，相当于数据库连接池，实际上是等待执行代码的一组线程，叫做工作者线程。Servlet 容器通过一个调度线程来管理工作者线程。当容器收到一个 Servlet 的访问请求，调度者线程就从线程池中选出一个工作者线程，将用户请求传递给该线程，然后由该线程处理 Servlet 的 service() 方法；当这个线程在执行的时候，容器收到一个新的请求，调度者线程再次从线程池中选出一个新的工作者线程；当容器同时收到对同一个 Servlet 的多个请求时，那么 Servlet 的 service() 方法将在多线程中并发执行。注：Servlet 容器默认采用单实例多线程的方式来处理请求。这样减少了产生 Servlet 实例的开销，提升了对请求的响应时间。对于 Tomcat 容器来讲，可以在其 server.xml 中通过 <Connector> 配置设置线程池中的线程数目。问题：Servlet 可不可以采用多实例多线程？或者多实例单线程？如何开发线程安全的 Servlet？Servlet 容器采用多线程来处理请求，提高性能的同时也造成了线程安全问题。要开发线程安全的 Servlet 应该从以下几个方面进行：变量的线程安全：多线程并不共享局部变量，所以我们要尽可能的在 Servlet 中使用局部变量。代码块的线程安全：使用同步块 synchronized，防止可能调用的代码块冲突。但是要注意的是，要尽可能缩小同步代码的范围，不要在 service() 方法和响应方法上直接使用同步，这会严重影响性能。属性的线程安全：注意 ServletContext、HttpSession、ServletRequest 对象中属性的安全性。使用同步集合：使用 Vector 代替 ArrayList，使用 Hashtable 代替 HashMap。避免自定义线程：不要在 Servlet 中创建自己的线程来完成某个功能。Servlet 本身就是多线程的，如果再创建新的线程，将会导致线程执行复杂化，出现线程安全问题。外部对象互斥：在多个 Servlet 中，对外部对象（比如文件）进行修改操作一定要加锁，做到互斥访问。SingleThreadModel 接口？javax.servlet.SingleThreadModel 接口是一个标识接口。如果一个…

Tomcat中Servlet的创建初始化体系结构及工作
type: item
date: 2026-07-07T08:31:34+00:00
description: Tomcat 中 Servlet 的创建初始化体系结构及工作一个 Web 应用对应一个 Context 容器。在 Tomcat 中，Context 容器是管理 Servlet 的核心，也是 Servlet 运行时的容器。当添加一个 Web 应用时，Tomcat 会创建一个 StandardContext 容器，并为其设置必要的参数。其中，url 和 path 分别代表该应用在 Tomcat 中的访问路径和实际的物理路径。Web 应用的初始化工作Tomcat 中 Web 应用的初始化工作主要在 ContextConfig 的 configureStart 方法中实现。初始化的核心任务是解析 web.xml 文件，该文件描述了 Web 应用的关键信息，是应用的入口。解析过程如下：查找全局配置：Tomcat 首先查找 globalWebXml，该文件位于 org/apache/catalina/startup/NO_DEFAULT_XML 或 conf/web.xml 中的任一个。查找宿主配置：接着查找 hostWebXml，即应用的配置文件（例如 examples/WEB-INF/web.xml）。解析与保存：web.xml 文件的各个配置项会被解析成相应的属性，保存在 WebXml 对象中。设置容器属性：随后将 WebXml 对象中的属性设置到 Context 容器中，包括创建 Servlet 对象、Filter、Listener 等。最后，Servlet 会被包装成 StandardWrapper 并作为子容器添加到 Context 中。因此，Context 容器才是真正运行 Servlet 的容器，其配置属性由应用的 web.xml 指定。创建 Servlet 对象如果 Servlet 的 load-on-startup 配置项大于 0，那么在 Context 容器启动时就会被实例化。前文提到，在解析配置文件时会读取默认的 globalWebXml。在 conf 下的 web.xml 文件中定义了一些默认配置项，其中定义了两个 Servlet：org.apache.catalina.servlets.DefaultServletorg.apache.jasper.servlet.JspServlet它们的 load-on-startup 分别是 1 和 3。这意味着当 Tomcat 启动时，这两个 Servlet 会被优先启动。JspServlet 主要用于将 JSP 页面转换成 Servlet，它会拦截所有以 .jsp 为后缀的文件。创建 Servlet 实例的方法是从 Wrapper.loadServlet 开始的。loadServlet 方法的主要任务是获取 servletClass，然后将其交给 InstanceManager 去创建一个基于 servletClass.class 的对象。初始化 Servlet由于 Servlet 在 Web 初始化时已经包装在 StandardWrapper 中，因此 Servlet 的初始化是在 StandardWrapper 的 initServlet 方法中进行的。该方法逻辑简单，主要是调用 Servlet 的 init 方法，同时将包装了 StandardWrapper 对象的 StandardWrapperFacade 作为 ServletConfig 传给 Servlet。如果该 Servlet 关联的是一个 JSP 文件，那么前面初始化的就是 JspServlet。接下来会模拟一次简单请求，调用这个 JSP 文件，以便将其编译为 class 文件，并初始化这个 class。Servlet 体系结构Java Web 应用是基于 Servlet 规范运转的。下面介绍一下 Servlet 的体系结构。顶层类关联从上图可以看出，Servlet 规范主要基于几个核心类运转。与 Servlet 主动关联的是三个类，分别是 ServletConfig、ServletRequest 和 ServletResponse。这三个类都是通过容器传递给 Servlet 的：ServletConfig：在 Servlet 初始化时传给 Servlet，主要用于获取 Servlet 的配置属性，这些属性可能在 Servlet 运行时被用到。ServletRequest 和 ServletResponse：在请求到达调用 Servlet 时传递过来。它们代表了请求和响应的具体对象，通常作为运输工具来传递交互结果。Servlet 的运行模式是一个典型的“握手型的交互式”运行模式。所谓“握手型的交互式”，是指两个模块为了交换数据通常都会准备一个交易场景，这个场景一直跟随交易过程直到交易完成。交易场景的…

Servlet 工作原理解析
type: item
date: 2026-07-07T08:31:18+00:00
description: 从 Servlet 容器说起要深入理解 Servlet，首先必须厘清 Servlet 容器（Servlet Container）的概念。Servlet 与 Servlet 容器的关系类似于“枪与子弹”：枪为子弹而生，子弹赋予枪杀伤力。二者彼此依存却又独立发展，这是工业化生产与标准化接口的结果。从技术角度而言，这种设计旨在实现解耦（Decoupling），通过标准化接口进行协作。既然接口是连接 Servlet 与 Servlet 容器的关键，我们将从接口层面展开分析。Servlet 容器作为独立发展的标准化产品，种类繁多且各有市场定位，难分优劣。例如，Jetty 在定制化和移动领域表现优异，而本文将以大家最为熟悉的 Tomcat 为例，介绍 Servlet 容器如何管理 Servlet。Tomcat 架构复杂，本文仅聚焦于 Servlet 与容器的接口部分，关于 Tomcat 的详细架构可参考相关文章《Tomcat 系统架构与模式设计分析》。在 Tomcat 的容器等级中，Context 容器直接管理 Servlet 在容器中的包装类 Wrapper。因此，Context 容器的运行方式将直接影响 Servlet 的工作模式。图 1. Tomcat 容器模型从上图可以看出，Tomcat 容器分为四个等级，真正管理 Servlet 的是 Context 容器。一个 Context 对应一个 Web 工程，这在 Tomcat 的配置文件中显而易见，如下所示：清单 1. Context 配置参数<Context path="/projectOne" docBase="D:\projects\projectOne" reloadable="true" />下面将详细介绍 Tomcat 解析 Context 容器的过程，包括如何构建 Servlet。Servlet 容器的启动过程Tomcat 7 开始支持嵌入式功能，增加了一个启动类 org.apache.catalina.startup.Tomcat。创建该类的实例对象并调用 start 方法即可启动 Tomcat。我们还可以通过该对象动态增加和修改配置参数，例如动态添加 Context、Servlet 等。下面利用这个 Tomcat 类来管理新增的一个 Context 容器，选择 Tomcat 7 自带的 examples Web 工程，观察它是如何加载到 Context 容器中的。清单 2. 给 Tomcat 增加一个 Web 工程Tomcat tomcat = getTomcatInstance(); File appDir = new File(getBuildDirectory(.html), "webapps/examples"); tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath(.html)); tomcat.start(); ByteChunk res = getUrl("http://localhost:" + getPort(.html) + "/examples/servlets/servlet/HelloWorldExample"); assertTrue(res.toString(.html).indexOf(" Hello World! ".html) > 0);清单 2 的代码创建了一个 Tomcat 实例并新增一个 Web 应用，然后启动 Tomcat 并调用其中的一个 HelloWorldExample Servlet，验证是否正确返回预期数据。Tomcat 的 addWebapp 方法代码如下：清单 3. Tomcat.addWebapppublic Context addWebapp(Host host, String url, String path.html) { silence(url.html); Context ctx = new StandardContext(); ctx.setPath(url.html); ctx.setDocBase(path.html); if (defaultRealm == null.html) { initSimpleAuth(); } ctx.setRealm(defaultRealm.html); ctx.addLifecycleListener(new DefaultWebXmlListener(.html)); ContextConfig ctxCfg = new ContextConfig(); ctx.addLifecycleListener(ctxCfg.html); ctxCfg.setDefaultWebXml("org…

Servlet工作原理解析
type: item
date: 2026-07-07T08:28:35+00:00
description: 从 Servlet 容器说起要介绍 Servlet 必须要先把 Servlet 容器说清楚，Servlet 与 Servlet 容器的关系有点像枪和子弹的关系，枪是为子弹而生，而子弹又让枪有了杀伤力。虽然它们是彼此依存的，但是又相互独立发展，这一切都是为了适应工业化生产的结果。从技术角度来说是为了解耦，通过标准化接口来相互协作。既然接口是连接 Servlet 与 Servlet 容器的关键，那我们就从它们的接口说起。前面说了 Servlet 容器作为一个独立发展的标准化产品，目前它的种类很多，但是它们都有自己的市场定位，很难说谁优谁劣，各有特点。例如现在比较流行的 Jetty，在定制化和移动领域有不错的发展，我们这里还是以大家最为熟悉 Tomcat 为例来介绍 Servlet 容器如何管理 Servlet。Tomcat 本身也很复杂，我们只从 Servlet 与 Servlet 容器的接口部分开始介绍，关于 Tomcat 的详细介绍可以参考我的另外一篇文章《 Tomcat 系统架构与模式设计分析》。Tomcat 的容器等级中，Context 容器是直接管理 Servlet 在容器中的包装类 Wrapper，所以 Context 容器如何运行将直接影响 Servlet 的工作方式。图 1 . Tomcat 容器模型从上图可以看出 Tomcat 的容器分为四个等级，真正管理 Servlet 的容器是 Context 容器，一个 Context 对应一个 Web 工程，在 Tomcat 的配置文件中可以很容易发现这一点，如下：清单 1 Context 配置参数<Context path="/projectOne " docBase="D:\projects\projectOne" reloadable="true" /> Show moreShow more icon下面详细介绍一下 Tomcat 解析 Context 容器的过程，包括如何构建 Servlet 的过程。Servlet 容器的启动过程Tomcat7 也开始支持嵌入式功能，增加了一个启动类 org.apache.catalina.startup.Tomcat。创建一个实例对象并调用 start 方法就可以很容易启动 Tomcat，我们还可以通过这个对象来增加和修改 Tomcat 的配置参数，如可以动态增加 Context、Servlet 等。下面我们就利用这个 Tomcat 类来管理新增的一个 Context 容器，我们就选择 Tomcat7 自带的 examples Web 工程，并看看它是如何加到这个 Context 容器中的。清单 2 . 给 Tomcat 增加一个 Web 工程Tomcat tomcat = getTomcatInstance(); File appDir = new File(getBuildDirectory(.html), "webapps/examples"); tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath(.html)); tomcat.start(); ByteChunk res = getUrl("http://localhost:" + getPort(.html) + "/examples/servlets/servlet/HelloWorldExample"); assertTrue(res.toString(.html).indexOf(" Hello World! ".html) > 0); Show moreShow more icon清单 1 的代码是创建一个 Tomcat 实例并新增一个 Web 应用，然后启动 Tomcat 并调用其中的一个 HelloWorldExample Servlet，看有没有正确返回预期的数据。Tomcat 的 addWebapp 方法的代码如下：清单 3 .Tomcat.addWebapppublic Context addWebapp(Host host, String url, String path.html) { silence(url.html); Context ctx = new StandardContext(); ctx.setPath( url .html); ctx.setDocBase(path.html); if (defaultRealm == null.html) { initSimpleAuth(); } ctx.setRealm(defaultRealm.html); ctx.addLifecycleListener(new DefaultWebXmlListener(.html))…

Servlet 3.0 新特性详解（servlet是单实例多线程的，线程池数量有限）
type: item
date: 2026-07-07T08:26:08+00:00
description: Servlet 3.0 新特性概述Servlet 3.0 作为 Java EE 6 规范体系中的一员，随 Java EE 6 规范一起发布。该版本在前一版本（Servlet 2.5）的基础上提供了若干新特性，旨在简化 Web 应用的开发和部署。其中有几项特性的引入让开发者感到非常兴奋，同时也获得了 Java 社区的一片赞誉之声：异步处理支持：有了该特性，Servlet 线程不再需要一直阻塞，直到业务处理完毕才能再输出响应，最后才结束该 Servlet 线程。在接收到请求之后，Servlet 线程可以将耗时的操作委派给另一个线程来完成，自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况，这将大大减少服务器资源的占用，并且提高并发处理速度。新增的注解支持：该版本新增了若干注解，用于简化 Servlet、过滤器（Filter）和监听器（Listener）的声明，这使得 web.xml 部署描述文件从该版本开始不再是必选的了。可插性支持：熟悉 Struts2 的开发者一定会对其通过插件的方式与包括 Spring 在内的各种常用框架的整合特性记忆犹新。将相应的插件封装成 JAR 包并放在类路径下，Struts2 运行时便能自动加载这些插件。现在 Servlet 3.0 提供了类似的特性，开发者可以通过插件的方式很方便地扩充已有 Web 应用的功能，而不需要修改原有的应用。下面我们将逐一讲解这些新特性。通过下面的学习，读者将能够明晰了解 Servlet 3.0 的变化，并能够顺利使用它进行日常的开发工作。异步处理支持Servlet 3.0 之前，一个普通 Servlet 的主要工作流程大致如下：首先，Servlet 接收到请求之后，可能需要对请求携带的数据进行一些预处理；接着，调用业务接口的某些方法，以完成业务处理；最后，根据处理的结果提交响应，Servlet 线程结束。其中第二步的业务处理通常是最耗时的，这主要体现在数据库操作，以及其它的跨网络调用等。在此过程中，Servlet 线程一直处于阻塞状态，直到业务方法执行完毕。在处理业务的过程中，Servlet 资源一直被占用而得不到释放，对于并发较大的应用，这有可能造成性能的瓶颈。对此，在以前通常是采用私有解决方案来提前结束 Servlet 线程，并及时释放资源。Servlet 3.0 针对这个问题做了开创性的工作。现在通过使用 Servlet 3.0 的异步处理支持，之前的 Servlet 处理流程可以调整为如下的过程：首先，Servlet 接收到请求之后，可能首先需要对请求携带的数据进行一些预处理；接着，Servlet 线程将请求转交给一个异步线程来执行业务处理，线程本身返回至容器，此时 Servlet 还没有生成响应数据；异步线程处理完业务以后，可以直接生成响应数据（异步线程拥有 ServletRequest 和 ServletResponse 对象的引用），或者将请求继续转发给其它 Servlet。如此一来，Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理，而是启动异步线程之后可以立即返回。异步处理特性可以应用于 Servlet 和过滤器两种组件。由于异步处理的工作模式和普通工作模式在实现上有着本质的区别，因此默认情况下，Servlet 和过滤器并没有开启异步处理特性。如果希望使用该特性，则必须按照如下的方式启用：对于使用传统的部署描述文件 (web.xml.html) 配置 Servlet 和过滤器的情况，Servlet 3.0 为 <servlet> 和 <filter> 标签增加了 <async-supported> 子标签。该标签的默认取值为 false，要启用异步处理支持，则将其设为 true 即可。以 Servlet 为例，其配置方式如下所示：<servlet> <servlet-name>DemoServlet</servlet-name> <servlet-class>footmark.servlet.DemoServlet</servlet-class> <async-supported>true</async-supported> </servlet>对于使用 Servlet 3.0 提供的 @WebServlet 和 @WebFilter 进行 Servlet 或过滤器配置的情况，这两个注解都提供了 asyncSupported 属性。默认该属性的取值为 false，要启用异步处理支持，只需将该属性设置为 true 即可。以 @WebFilter 为例，其配置方式如下所示：@WebFilter(urlPatterns = "/demo", asyncSupported = true.html) public class DemoFilt…

Spring MVC 3.2 技术预览（一）：Servlet 3介绍，异步支持
type: item
date: 2026-07-07T08:25:16+00:00
description: 概述Spring MVC 3.2 M1 引入了基于 Servlet 3.0 支持的异步请求处理功能。我将针对 Spring MVC 3.2 的新特性发布一系列文章，通过对背景知识和相关内容的充分介绍，帮助你理解为什么需要这些新特性，以及如何使用它们。这是系列文章中的第一篇。Spring MVC 3.2 的更新内容已经可以在 Spring Framework Github 中查看。你也可以将 http://repo.springsource.org/snapshot 配置到你的项目仓库中，以获取快照版本。在后续文章中，我将提供更多的源码示例链接。如果你想现在就尝试这些新特性，可以在 GitHub 上签出 spring-mvc-async 分支中的 spring-mvc-showcase 项目，并通过 提交记录 查看其中的更新信息。特性预览从编程模型的角度来看，新功能看似简单。现在，控制器（Controller）的方法可以返回 Callable 类型来完成异步请求处理。Spring MVC 3.2 会在 TaskExecutor 的帮助下，在一个独立的线程中调用这个返回值。示例代码如下：@RequestMapping(method = RequestMethod.POST) public Callable<String> processUpload(final MultipartFile file.html) { return new Callable<String>() { public Object call() throws Exception { // ... return "someView"; } }; }另一种方式是在控制器方法中返回 DeferredResult 类型（这是 Spring MVC 3.2 中的新成员），并在任意线程中完成异步处理。例如，对某个外部事件（如 JMS 消息、AMQP 消息、Redis 消息等）作出反应。下面是另一个代码片段：@RequestMapping("/quotes".html) @ResponseBody public DeferredResult quotes() { DeferredResult deferredResult = new DeferredResult(); // Add deferredResult to a Queue or a Map... return deferredResult; } // In some other thread.. // Set the return value on the deferredResult deferredResult.set(data.html);大家可能对上面的代码片段有很多疑问，我会在后续文章中给出更多的细节信息。在深入了解之前，我先介绍一些相关的技术背景知识。异步请求背景：长连接与线程模型当前一些网络应用最常用的异步处理方式就是长连接方式，例如运行一个缓慢的数据库查询、调用一个外部的 REST API 或者执行其他 I/O 操作。这些方式很快就会消耗光 Servlet 容器的线程池，影响程序的可扩展性。在一些情况下，你可能需要等待一个处理完成，例如发送邮件、执行数据库操作等。在这种“即发即忘”（fire-and-forget）情况下，你可以使用 Spring 注解 @Async 或设置 Spring Integration 事件并迅速返回，也许还可以返回一个用于确认的 ID，为后续的响应所用。这在 Spring MVC 3.2 之前就可以实现，并且可以避免请求死锁。对于需要在结果返回之前释放资源的其他情况，你需要先释放处理请求的 Servlet 容器线程来提高程序的可扩展性。为了实现这个功能，Servlet 3 允许一个 Servlet 在返回请求之后声明保持响应为打开状态，这样请求就可以在一个独立线程中完成。具体来说，可以调用 Servlet 3 中的 request.startAsync() 方法，并使用返回的 AsyncContext 在一个独立线程内继续写入（并最终完成）响应。这在客户端看来没有任何变化，请求仍然看起来像是其他 HTTP 标准的“请求—响应”一样。但是，在服务器端看来，异步请求处理可以让你以扩展性更好的方式处理请求。Servlet 3 规范定义了异步请求处理的标准流程。关于 Servlet 对异步请求处理的支持，还有很多内容，你可以找到 很多 示例 和 文章（注：部分外链可能因网络原因无法访问），但是上面总结的这些是所需要的最基本的概念。总结在下一篇文章中，我将介绍第二种异步请求处理的方式：客户端浏览器无延时的实时获取服务器的更新信息。过去已经发展出了很多方式实现这个功能，一些停留在…