ServletContextInitializer

ServletContextInitializer 是 Spring Boot 提供的一个接口,允许你在内嵌 Servlet 容器(如 Tomcat)启动时,自定义注册 Servlet、Filter、Listener 或修改 ServletContext 属性。
@FunctionalInterface
public interface ServletContextInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
这个接口会在内嵌容器启动时自动被调用(等价于 web.xml 的内容)。
它和Bean的生命周期的关系?
它不是 Bean 的生命周期钩子,但它是由 Spring Boot 的 ServletWebServerApplicationContext 在 Web 容器启动前主动调用的。
大概在 Spring 完成 ApplicationContext.refresh() 时:
// 调用所有 ServletContextInitializer 的 onStartup()
this.selfInitialize(servletContext);
Lab
注册一个原始 Servlet(不使用 @WebServlet)
@Component
public class MyServletInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) {
ServletRegistration.Dynamic servlet = servletContext.addServlet("myServlet", new MyCustomServlet());
servlet.addMapping("/custom");
servlet.setLoadOnStartup(1);
}
}
等效于 web.xml 的配置:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.MyCustomServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/custom</url-pattern>
</servlet-mapping>
注册一个 Filter
@Component
public class MyFilterInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) {
FilterRegistration.Dynamic filter = servletContext.addFilter("myFilter", new MyFilter());
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
注册一个 Listener
@Component
public class MyListenerInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) {
servletContext.addListener(MySessionListener.class);
}
}
Spring Boot 自动适配的变种接口
Spring Boot 自动支持以下扩展:
| 接口 / 注解 | 说明 |
|---|---|
| ServletContextInitializer | 最底层的原始注册能力 |
| @ServletComponentScan+@WebServlet/@WebFilter/@WebListener | 注解式注册(Spring Boot 自动支持) |
| FilterRegistrationBean,ServletRegistrationBean | Spring Boot 提供的注册工具类 |
FilterRegistrationBean
这本质上最终也会被转换为 ServletContextInitializer 调用。
@Bean
public FilterRegistrationBean<MyFilter> myFilter() {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new MyFilter());
registration.addUrlPatterns("/*");
return registration;
}
常见高级用途
| 目标 | 实现方式 |
|---|---|
| 注册动态 SSE Servlet | 用ServletContextInitializer注册 SSE Servlet |
| 手动配置 ServletContext 的参数 | servletContext.setInitParameter("xxx", "yyy") |
| 与非 Spring 的旧 Servlet 框架桥接 | 注册第三方原生 Servlet(如 Swagger、Shiro) |
| 替代 web.xml | 全部 Servlet/Filter/Listener 配置转移到 Java |
注意
springboot内嵌的tomcat容器,是无法在启动之后动态add一个servlet的
private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet,
Map<String,String> initParams) throws IllegalStateException {
if (servletName == null || servletName.isEmpty()) {
throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", servletName));
}
// 这里会检查容器状态,只有容器启动之前能够注册,启动之后注册会报错 IllegalStateException
checkState("applicationContext.addServlet.ise");
Wrapper wrapper = (Wrapper) context.findChild(servletName);
// Assume a 'complete' ServletRegistration is one that has a class and
// a name
if (wrapper == null) {
wrapper = context.createWrapper();
wrapper.setName(servletName);
context.addChild(wrapper);
} else {
if (wrapper.getName() != null && wrapper.getServletClass() != null) {
if (wrapper.isOverridable()) {
wrapper.setOverridable(false);
} else {
return null;
}
}
}
ServletSecurity annotation = null;
if (servlet == null) {
wrapper.setServletClass(servletClass);
Class<?> clazz = Introspection.loadClass(context, servletClass);
if (clazz != null) {
annotation = clazz.getAnnotation(ServletSecurity.class);
}
} else {
wrapper.setServletClass(servlet.getClass().getName());
wrapper.setServlet(servlet);
if (context.wasCreatedDynamicServlet(servlet)) {
annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
}
}
if (initParams != null) {
for (Map.Entry<String,String> initParam : initParams.entrySet()) {
wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
}
}
ServletRegistration.Dynamic registration = new ApplicationServletRegistration(wrapper, context);
if (annotation != null) {
registration.setServletSecurity(new ServletSecurityElement(annotation));
}
return registration;
}
标题:ServletContextInitializer
作者:Gakkiyomi2019
地址:https://gakkiyomi.blog/articles/2025/07/15/1752548212181.html
看不懂,大佬
孩子,这并不是tomcat
666666666 羡慕大佬手撕tomact
大佬
大佬
大佬
终于有技术佬了吗
佬
又是逃课大g老师讲堂的一天
前端叉出去
这不是源码啊,这是应用代码啊,源码我配吗?
又是逃课大g老师讲堂的一天
又是大g老师带我读源码的一天
佬儿
不愧是g大佬
G佬 tql
牛!
不明觉立
佬!
虽然看不懂 但肯定很厉害
厉害 G老师
不明觉厉!🐂🐸
看不懂,大佬