Spring 3.0+ 引入的純註解配置模式,通過 WebApplicationInitializer 機制實現無 web.xml 啟動。本文基於 Spring 4.3/5.x 源碼,深度追蹤從 Servlet 容器啟動 → SpringServletContainerInitializer → WebApplicationInitializer 實現類 → AbstractAnnotationConfigDispatcherServletInitializer → 根/子容器創建 → DispatcherServlet 初始化的完整鏈路,並解析關鍵模板方法設計模式應用。
Servlet 3.0 引入 ServletContainerInitializer (SCI) 機制:容器啟動時掃描類路徑下 META-INF/services/javax.servlet.ServletContainerInitializer 文件,實例化並調用 onStartup(Set<Class<?>>, ServletContext)。
# META-INF/services/javax.servlet.ServletContainerInitializer
org.springframework.web.SpringServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class) // 關鍵:告知容器傳遞所有實現類
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new ArrayList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// 過濾:非接口、非抽象、可實例化、實現 WebApplicationInitializer
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers())
&& WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
} catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected");
return;
}
// 關鍵:按 @Order 排序
AnnotationAwareOrderComparator.sort(initializers);
// 依次啟動
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
// 實現層次:
// WebApplicationInitializer (接口)
// └── AbstractContextLoaderInitializer (抽象基類)
// └── AbstractDispatcherServletInitializer (DispatcherServlet 專用)
// └── AbstractAnnotationConfigDispatcherServletInitializer (註解配置專用)
// └── 用戶自定義類 (如 WebMvcInit)
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
// 1. 子類實現:創建根 ApplicationContext
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
// 2. 註冊 ContextLoaderListener (綁定容器生命週期)
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
} else {
logger.debug("No ContextLoaderListener registered");
}
}
// 模板方法:由子類實現
protected abstract WebApplicationContext createRootApplicationContext();
protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
return null;
}
}
public abstract class AbstractDispatcherServletInitializer
extends AbstractContextLoaderInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
// 創建根容器 (Service、DAO、DataSource 等業務組件)
Class<?>[] configClasses = getRootConfigClasses();
if (configClasses != null && configClasses.length > 0) {
return createContext(configClasses);
}
return null;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 1. 先創建根容器 (父類邏輯)
super.onStartup(servletContext);
// 2. 創建 Servlet 容器 (Controller、ViewResolver、Interceptor 等 Web 組件)
WebApplicationContext servletAppContext = createServletApplicationContext();
// 3. 註冊 DispatcherServlet
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
ServletRegistration.Dynamic registration = servletContext.addServlet(
getServletName(), dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
// 4. 註冊 Filter (如 HiddenHttpMethodFilter, CharacterEncodingFilter)
for (Filter filter : getServletFilters()) {
registerServletFilter(servletContext, filter);
}
// 5. 自定義 ServletContext 初始化
customizeRegistration(registration);
}
// 模板方法:由子類實現
protected abstract Class<?>[] getRootConfigClasses();
protected abstract Class<?>[] getServletConfigClasses();
protected abstract String[] getServletMappings();
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
return new DispatcherServlet(servletAppContext);
}
protected Filter[] getServletFilters() { return new Filter[0]; }
protected void customizeRegistration(ServletRegistration.Dynamic registration) {}
}
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (configClasses != null && configClasses.length > 0) {
return createContext(configClasses);
}
return null;
}
@Override
protected WebApplicationContext createServletApplicationContext() {
Class<?>[] configClasses = getServletConfigClasses();
if (configClasses != null && configClasses.length > 0) {
return createContext(configClasses);
}
return null;
}
// 核心:創建 AnnotationConfigWebApplicationContext
protected WebApplicationContext createContext(Class<?>[] configClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses); // 註冊配置類
return context;
}
}
@Order(1) // 多初始化器時控制順序
public class WebMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
// 根容器配置類 (Service, Repository, DataSource, Security 等)
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class, WebSecurityConfig.class };
}
// Servlet 容器配置類 (Controller, ViewResolver, Interceptor 等)
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
// DispatcherServlet 映射路徑
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// 註冊 Filter
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new HiddenHttpMethodFilter(),
new CharacterEncodingFilter("UTF-8", true)
};
}
}
// RootConfig.java - 根容器
@Configuration
@ComponentScan(basePackages = "com.example.service",
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Controller.class))
@EnableTransactionManagement
@EnableAspectJAutoProxy
@Import({DataSourceConfig.class, MyBatisConfig.class, RedisConfig.class})
public class RootConfig { }
// WebConfig.java - Servlet 容器
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.web",
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = Controller.class))
@Import({MvcConfig.class, ViewResolverConfig.class, InterceptorConfig.class})
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
}
}
| 特性 | 根容器 (Root WebApplicationContext) | Servlet 容器 (DispatcherServlet Context) |
|---|---|---|
| 創建時機 | ContextLoaderListener.contextInitialized() | DispatcherServlet.init() |
| 父子關係 | 父容器 | 子容器 (parent = 根容器) |
| Bean 掃描範圍 | @Service, @Repository, @Component, @Configuration | @Controller, @RestController, @ControllerAdvice |
| 典型組件 | Service、DAO、DataSource、TransactionManager、Security | ViewResolver、HandlerMapping、HandlerAdapter、Interceptor |
| Bean 可見性 | 不可訪問子容器 Bean | 可訪問父容器 Bean (Service 注入 Controller) |
| 銷毀順序 | 後銷毀 | 先銷毀 |
Tomcat 啟動
↓
StandardContext.startInternal()
↓
ServletContainerInitializer.onStartup() [SpringServletContainerInitializer]
↓
掃描 @HandlesTypes(WebApplicationInitializer.class) 所有實現類
↓
實例化並按 @Order 排序
↓
for (initializer : initializers) initializer.onStartup(servletContext)
↓
[用戶類] WebMvcInit.onStartup()
↓
super.onStartup() → AbstractDispatcherServletInitializer.onStartup()
↓
1. super.onStartup() → AbstractContextLoaderInitializer.onStartup()
↓ registerContextLoaderListener()
↓ createRootApplicationContext() [模板方法]
↓ AnnotationConfigWebApplicationContext.register(rootConfigClasses)
↓ ContextLoaderListener 綁定根容器生命週期
↓
2. createServletApplicationContext()
↓ AnnotationConfigWebApplicationContext.register(servletConfigClasses)
↓
3. new DispatcherServlet(servletAppContext)
↓
4. servletContext.addServlet("dispatcher", dispatcherServlet)
↓ setLoadOnStartup(1), addMapping("/"), setAsyncSupported(true)
↓
5. 註冊 Filters (CharacterEncodingFilter, HiddenHttpMethodFilter...)
↓
DispatcherServlet.init() → 初始化 9 大組件 (HandlerMapping, HandlerAdapter, ViewResolver...)
↓
容器啟動完成,準備接收請求
| 問題 | 原因 | 排查方向 |
|---|---|---|
| WebApplicationInitializer 不生效 | 未在類路徑、打包漏掉、Servlet 版本 < 3.0 | 檢查 web.xml metadata-complete="false"、Maven 打包包含類、Tomcat 7+ |
| 根容器 Bean 重複創建 | ComponentScan 範圍重疊、未排除 Controller | 根容器 excludeFilters=Controller,Servlet 容器 includeFilters=Controller |
| DispatcherServlet 映射攔截靜態資源 | 映射為 "/" 而非 "/*" | 使用 "/",配合 DefaultServletHandler 配置靜態資源處理 |
| Filter 不生效 | getServletFilters 返回空、順序錯誤 | CharacterEncodingFilter 必須在最前、HiddenHttpMethodFilter 其次 |
| 雙親委派導致 Bean 覆蓋 | 父子容器掃描同一包 | 嚴格分離掃描包路徑,或使用 @ConditionalOnMissingBean |
@SpringBootApplication // 組合:@Configuration + @EnableAutoConfiguration + @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 內部實現:
// 1. 推斷 Web 類型 (Servlet/Reactive)
// 2. 創建 ApplicationContext (AnnotationConfigServletWebServerApplicationContext)
// 3. 準備環境、打印 Banner
// 4. 刷新上下文 → 自動配置 → 嵌入式 Tomcat 啟動 → DispatcherServlet 註冊
// 5. CommandLineRunner/ApplicationRunner 回調
Spring 純註解啟動鏈路:ServletContainerInitializer (SPI) → SpringServletContainerInitializer (@HandlesTypes) → WebApplicationInitializer 實現類 → AbstractContextLoaderInitializer (根容器) → AbstractDispatcherServletInitializer (DispatcherServlet + 子容器) → AbstractAnnotationConfigDispatcherServletInitializer (註解配置類註冊) → 用戶配置類。核心設計模式:模板方法模式 (定義啟動骨架,子類填充配置類)、策略模式 (不同 WebApplicationInitializer 實現)、父子容器隔離 (職責分離、Bean 可見性控制)。理解此流程,能精準排查啟動失敗、Bean 重複、Filter 不生效、靜態資源攔截等疑難雜症,也為閱讀 Spring Boot 自動配置源碼奠定基礎。