前几天有个朋友找我吐槽:“小米,我在面试的时候被问到 Tomcat 是怎么创建 Servlet 实例的?我当时只说了‘反射’,结果面试官笑了笑,说:你知道 Tomcat 为什么能用反射吗?它背后做了哪些事情?’——我当场懵了!”
我听完差点没笑出声。因为这道题,真的太有代表性了。
它看起来像是在考“Java 基础”,但其实考的是你对 Servlet 容器底层机制的理解深度。
今天就来聊聊这个问题——
Tomcat 容器是如何创建 Servlet 类实例的?它到底用了什么原理?
故事从一个请求开始说起
假设我们有个简单的 HelloServlet,部署在 Tomcat 里。
当浏览器第一次请求 /hello 时,Tomcat 会检查对应的 Servlet 是否已经存在。
如果还没有,那它就会开始执行一系列动作:
- 加载 Servlet 类
- 创建 Servlet 实例
- 调用 init() 初始化
- 缓存起来,供后续请求复用
听起来是不是挺像 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。它的核心逻辑其实就两步:
- 用类加载器加载 Servlet 类;
- 用反射创建实例
示意伪代码:
但有个关键点: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岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!