首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 註解配置啟動流程深度解析:從 WebApplicationInitializer 到容器刷新全鏈路

     編輯:Java 相關知識

Spring 3.0+ 引入的純註解配置模式,通過 WebApplicationInitializer 機制實現無 web.xml 啟動。本文基於 Spring 4.3/5.x 源碼,深度追蹤從 Servlet 容器啟動 → SpringServletContainerInitializer → WebApplicationInitializer 實現類 → AbstractAnnotationConfigDispatcherServletInitializer → 根/子容器創建 → DispatcherServlet 初始化的完整鏈路,並解析關鍵模板方法設計模式應用。

Servlet 3.0+ 容器啟動機制

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

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);
        }
    }
}

WebApplicationInitializer 接口與核心實現體系

public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

// 實現層次:
// WebApplicationInitializer (接口)
    // └── AbstractContextLoaderInitializer (抽象基類)
            //     └── AbstractDispatcherServletInitializer (DispatcherServlet 專用)
                    //         └── AbstractAnnotationConfigDispatcherServletInitializer (註解配置專用)
                            //             └── 用戶自定義類 (如 WebMvcInit)

AbstractContextLoaderInitializer:根容器創建

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;
    }
}

AbstractDispatcherServletInitializer:DispatcherServlet 註冊

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) {}
}

AbstractAnnotationConfigDispatcherServletInitializer:註解配置專用

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("/**");
    }
}

容器層級關係與 Bean 可見性

特性根容器 (Root WebApplicationContext)Servlet 容器 (DispatcherServlet Context)
創建時機ContextLoaderListener.contextInitialized()DispatcherServlet.init()
父子關係父容器子容器 (parent = 根容器)
Bean 掃描範圍@Service, @Repository, @Component, @Configuration@Controller, @RestController, @ControllerAdvice
典型組件Service、DAO、DataSource、TransactionManager、SecurityViewResolver、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

Spring Boot 時代的啟動簡化

@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 自動配置源碼奠定基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved