繼承 AbstractAnnotationConfigDispatcherServletInitializer 後,一個普通 Java 類如何接管整個 Spring 容器啟動?本文通過源碼追蹤,完整還原 Servlet 3.0+ 規范 + Spring SPI 機制 + Tomcat 容器 三方協作的啟動鏈路。
@Order(1)
public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class, WebSecurityConfig.class };
}
@Override protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
@Override protected String[] getServletMappings() { return new String[] { "/" }; }
@Override protected Filter[] getServletFilters() {
return new Filter[] { new HiddenHttpMethodFilter() };
}
}
WebMvcInit (用戶代碼)
↓ extends
AbstractAnnotationConfigDispatcherServletInitializer
↓ implements WebApplicationInitializer
AbstractContextLoaderInitializer
↓ onStartup(ServletContext)
→ registerContextLoaderListener()
→ createRootApplicationContext() 【關鍵:創建根容器】
→ new AnnotationConfigWebApplicationContext()
→ register(configClasses) --> 掃描 @Configuration/@Component
→ ContextLoaderListener 綁定到 ServletContext
↓ extends
AbstractDispatcherServletInitializer
↓ onStartup(ServletContext)
→ registerDispatcherServlet()
→ createServletApplicationContext() 【關鍵:創建 Web 容器】
→ new AnnotationConfigWebApplicationContext()
→ register(servletConfigClasses)
→ new DispatcherServlet(rootContext)
→ servletContext.addServlet("dispatcher", dispatcher)
→ mapping.addUrlPatterns("/")
AbstractContextLoaderInitializer — 根容器啟動public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext); // 模板方法
}
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext(); // 子類實現
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener); // 註冊到 Servlet 容器
}
}
protected abstract WebApplicationContext createRootApplicationContext();
}
AbstractAnnotationConfigDispatcherServletInitializer — 實現模板方法@Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses(); // 用戶返回 RootConfig.class 等
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses); // 註冊配置類,觸發掃描
return rootAppContext;
}
return null;
}
SpringServletContainerInitializer — Spring 的 SPI 入口@HandlesTypes(WebApplicationInitializer.class) // 掃描所有實現類
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// 過濾接口、抽象類、非實現類
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers())
&& WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
}
}
}
AnnotationAwareOrderComparator.sort(initializers); // @Order 排序
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext); // 統一調用
}
}
}
StandardContext.startInternal()Tomcat 啟動
│
├─> 掃描 META-INF/services/javax.servlet.ServletContainerInitializer
│ └─> 發現 SpringServletContainerInitializer
│
├─> SpringServletContainerInitializer.onStartup()
│ ├─> @HandlesTypes 掃描類路徑下所有 WebApplicationInitializer 實現類
│ ├─> 實例化並按 @Order 排序
│ └─> 依次調用 onStartup(ServletContext)
│
├─> AbstractContextLoaderInitializer.onStartup()
│ └─> registerContextLoaderListener()
│ └─> createRootApplicationContext() --> AnnotationConfigWebApplicationContext
│ └─> register(RootConfig.class, WebSecurityConfig.class)
│ └─> 掃描 @Component/@Configuration/@Bean --> 根容器就緒
│ └─> ContextLoaderListener 綁定到 ServletContext
│
└─> AbstractDispatcherServletInitializer.onStartup()
└─> registerDispatcherServlet()
├─> createServletApplicationContext() --> AnnotationConfigWebApplicationContext
│ └─> register(WebConfig.class) --> 掃描 Controller、ViewResolver
├─> new DispatcherServlet(rootWebAppContext)
├─> servletContext.addServlet("dispatcher", dispatcher)
└─> mapping.addUrlPatterns("/") --> Web 容器就緒
ServletContainerInitializer 介面調用WebApplicationInitializer,@HandlesTypes 一網打盡@Order + AnnotationAwareOrderComparator 保證根容器先於 Web 容器啟動SpringServletContainerInitializer.onStartup() 打斷點,觀察 webAppInitializerClasses 集合AbstractContextLoaderInitializer.registerContextLoaderListener() 打斷點,檢查 rootAppContext Bean 定義數量-Ddebug 可看到 Tomcat 詳細的初始化日誌「框架的魔法,其實只是『約定優於配置』+『標準化 SPI』+『模板方法模式』的教科書級組合。」
下一步可深入:AnnotationConfigWebApplicationContext 的 refresh() 流程(BeanDefinition 註冊 → BeanPostProcessor → 實例化 → 注入 → 初始化)、DispatcherServlet.doDispatch() 請求處理鏈路。歡迎關注系列源碼解析!🔍