web.xml 中 contextConfigLocation、ContextLoaderListener、DispatcherServlet 宣告順序是否影響啟動?applicationContext.xml (父容器) 總先於 spring-mvc-servlet.xml (子容器) 初始化?Jetty 原始碼鏈路證明:
Server.start()
→ AbstractLifeCycle.start()
→ WebAppContext.doStart()
→ WebAppContext.startContext()
→ ServletContextHandler.startContext() // 關鍵分水嶺
→ ContextHandler.startContext() // 1. 初始化所有 Listener (含 ContextLoaderListener)
→ ServletHolder.initialize() // 2. 初始化 Servlet (含 DispatcherServlet)

contextInitialized(ServletContextEvent) 入口ContextLoader.initWebApplicationContext()WebApplicationContext.ROOT 為空 → 建立 XmlWebApplicationContextconfigAndRefresh() → 讀取 contextConfigLocation → AbstractApplicationContext.refresh() 完成 Bean 定義載入、實例化、注入FrameworkServlet.initServletBean() 入口initWebApplicationContext() 建立 XmlWebApplicationContextwac.setParent(rootContext)refresh() 掃描 spring-mvc-servlet.xml (僅掃描 @Controller 等 Web 組件)<!-- applicationContext.xml: 業務層、數據層 -->
<context:component-scan base-package="com.example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- spring-mvc-servlet.xml: 僅掃描 Web 層 -->
<context:component-scan base-package="com.example.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
Jetty 遵循 Servlet 規範:Listener 初始化 → Filter 初始化 → Servlet 初始化。此順序由容器生命週期模板方法強制保證,與 web.xml 宣告順序無關。父子容器雙層架構是 Spring MVC 設計的基石,正確配置掃描範圍是避免 Bean 重複、循環依賴的關鍵。