学会这一篇,让你彻底拿下“Tomcat 如何创建 Servlet”面试题!

38 阅读6分钟



前几天有个朋友找我吐槽:“小米,我在面试的时候被问到 Tomcat 是怎么创建 Servlet 实例的?我当时只说了‘反射’,结果面试官笑了笑,说:你知道 Tomcat 为什么能用反射吗?它背后做了哪些事情?’——我当场懵了!”

我听完差点没笑出声。因为这道题,真的太有代表性了。

它看起来像是在考“Java 基础”,但其实考的是你对 Servlet 容器底层机制的理解深度

今天就来聊聊这个问题——

Tomcat 容器是如何创建 Servlet 类实例的?它到底用了什么原理?

故事从一个请求开始说起

假设我们有个简单的 HelloServlet,部署在 Tomcat 里。

当浏览器第一次请求 /hello 时,Tomcat 会检查对应的 Servlet 是否已经存在。

如果还没有,那它就会开始执行一系列动作:

  1. 加载 Servlet 类
  2. 创建 Servlet 实例
  3. 调用 init() 初始化
  4. 缓存起来,供后续请求复用

听起来是不是挺像 Spring 创建 Bean 的过程?

其实,这俩机制确实有点像,只不过 Tomcat 是在 Web 层面负责“管理 Servlet 实例”的容器。

Servlet 是谁创建的?

这个问题的关键其实是搞清楚:“谁来 new 这个 Servlet?”

很多人第一反应是“Tomcat”,但 Tomcat 只是一个整体框架。真正负责创建 Servlet 实例的,是它内部的 Wrapper 容器

Tomcat 的容器体系是分层的,大概可以这么理解:

Server -> Service -> Engine -> Host -> Context -> Wrapper

  • Server:顶级容器,表示整个服务器。
  • Service:一个服务,通常对应一个连接器(Connector)。
  • Engine:处理请求的引擎。
  • Host:虚拟主机。
  • Context:一个 Web 应用(对应一个 Web 项目)。
  • Wrapper:包装每一个 Servlet 的小容器。

所以,当 Tomcat 需要创建 Servlet 实例时,是由 Wrapper(通常是 StandardWrapper) 来完成的。

创建实例的关键方法

那具体是怎么创建的呢?

我们来看看 StandardWrapper 里最关键的一个方法:

注意看这一行:

是不是很眼熟?对,就是反射

但是,这个 instanceManager 可不是简单的 Class.newInstance(),它背后封装了更多逻辑,比如:

  • 处理类加载器隔离;
  • 支持 JNDI 注入;
  • 支持注解(如 @WebServlet、@Resource);
  • 支持自定义实例化逻辑。

也就是说,Tomcat 在创建 Servlet 时,不是“裸用反射”,而是用InstanceManager进行了“安全、可扩展”的封装。

InstanceManager 的幕后故事

Tomcat 的 InstanceManager 有多个实现,比如:

  • DefaultInstanceManager(默认实现)
  • LocalStringsManager(多语言支持)
  • AnnotationProcessor(支持注解注入)

最常见的实现是 DefaultInstanceManager。它的核心逻辑其实就两步:

  1. 用类加载器加载 Servlet 类;
  2. 用反射创建实例

示意伪代码:

但有个关键点:Tomcat 的类加载器不是普通的系统类加载器

每个 Web 应用都有自己的 WebappClassLoader,它可以隔离不同项目之间的类,防止冲突。

比如两个项目都引用了不同版本的同一个依赖,Tomcat 就靠这个机制保证它们互不干扰。

所以,InstanceManager 创建实例时,实际上是通过 Web 应用专属的类加载器 来加载 Servlet 类。

那为什么说 Tomcat 不只是“反射”?

因为 Tomcat 做的事远比你想象的多:

1、安全控制

  • Tomcat 在反射前后会通过 SecurityManager 检查权限,避免恶意代码执行。

2、依赖注入支持

  • Tomcat 会解析注解,比如 @Resource、@WebInitParam,并在创建后注入相关资源。

3、生命周期管理

  • Servlet 的整个生命周期(init → service → destroy)都由容器控制。
  • 当应用关闭时,Tomcat 会自动调用 destroy(),并清理实例。

4、实例复用

  • Tomcat 默认每个 Servlet 只创建一个实例(除非配置 SingleThreadModel,不过这个接口早就废弃了)。

所以面试官问“用了什么原理”,你不能只说“反射”,而应该答:

Tomcat 通过 StandardWrapper 调用 InstanceManager,使用反射机制创建 Servlet 实例,并结合自定义类加载器、注解解析、依赖注入和安全控制,完成 Servlet 的生命周期管理。

——这就叫有深度的回答。

类加载器隔离的精妙之处

讲到类加载器,这块真的太多面试官爱问了。

Tomcat 的类加载层次大致如下:

WebappClassLoader 是最关键的。它会优先从当前 Web 应用的 WEB-INF/classes 和 WEB-INF/lib 目录加载类,如果找不到,才会往上层找。

这就是为什么两个不同项目可以使用不同版本的 Spring,而互不冲突。

所以,Tomcat 创建 Servlet 时的那句“newInstance”,其实是发生在这个独立的类加载环境里的。

和 Spring 的 Bean 创建有什么不同?

很多人会问:Tomcat 创建 Servlet 实例和 Spring 容器创建 Bean 有啥区别?

简单对比下:

可以看到,Tomcat 管的是“Web 层的容器”,Spring 管的是“应用层的容器”,两者相辅相成。

很多时候,Spring 的 DispatcherServlet 本身也是由 Tomcat 创建的 Servlet 实例之一。

总结一下

所以,当你被问到“Tomcat 是怎么创建 Servlet 实例的”时,别再简单说“反射”了。

完整的思路应该是这样的:

Tomcat 的容器层次中,Wrapper 负责一个 Servlet 的管理;

第一次请求时,StandardWrapper 调用 loadServlet();

通过 InstanceManager 加载类并使用反射创建实例;

由 WebappClassLoader 提供隔离的类加载环境;

初始化 Servlet 并管理其生命周期;

同时处理注解注入、安全检查、资源绑定等逻辑。

一句话总结:

Tomcat 创建 Servlet 实例的原理,是 反射 + 类加载器 + 生命周期管理 + 注解注入 的综合实现。

END

每次写到这种底层原理,我都觉得 Tomcat 特别像个默默无闻的“管家”——

它不直接写业务逻辑,却在背后为每一个 Servlet 打理好一切:加载、注入、回收、复用。

当你能理解它如何“创造”一个 Servlet 的那一刻,你其实也在理解 Java Web 世界的根基。

所以,下一次面试再被问到这个问题时,记得微微一笑,然后淡定地说出那句——

“Tomcat 的 Servlet 实例是由 StandardWrapper 通过 InstanceManager 结合反射机制创建的,背后还包括类加载器隔离与生命周期管理。”

面试官听完,大概率会露出一个赞许的笑容。

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!