web.xml中按XML从上到下顺序加载,先于Servlet和Filter;同一类重复声明会创建多个实例;web.xml配置优先级高于@WebListener注解。
web.xml 中
的加载顺序由配置顺序决定
容器(如 Tomcat)按
中
元素出现的**从上到下顺序**依次实例化并调用
。这不是“优先级”或“依赖声明”,就是纯文本顺序。
多个
块,谁写在前面,谁先初始化
同一个类被重复声明两次?会创建两个实例,且按两次出现的位置分别触发
不支持通过属性(如
)控制顺序;XML Schema 里没这字段
如果依赖关系复杂(比如 A 要用 B 初始化后的上下文属性),只能靠人工排位置,别指望容器自动拓扑排序
Listener 加载早于 Servlet 和 Filter
是整个 Web 应用生命周期最早介入的扩展点之一——比
的
、也比所有
都早。这意味着:
已创建,但
还不存在(因为还没到 Servlet 阶段)
不能在
里调用
或访问具体 Servlet 实例
适合做全局资源预热(如连接池初始化、缓存加载)、监听器自身注册(如
)
若在此阶段抛出异常,整个应用启动失败,日志里通常会带
和
与注解 @WebListener 冲突时,web.xml 优先级更高
当同时存在 XML 配置和
注解时,Servlet 规范明确要求:web.xml 中声明的
**必须先于** 所有注解发现的 Listener 加载。
也就是说,即使你写了
,它也排在所有 XML 里的
后面
混合使用极易导致顺序误判——建议二选一:全 XML 或全注解(推荐后者,更可控)
Tomcat 9+ 对注解 Listener 支持稳定,但某些老版本或 WebLogic 等商用容器对
的发现时机可能有偏差
验证方式:在每个 Listener 的
里打日志,看输出顺序
常见陷阱:Listener 拿不到 Spring 上下文
很多人想在
里直接获取 Spring 的
,结果得到
——因为 Spring 的
本身就是一个
,它只是“恰好”常被写在最前面,但不是魔法。
如果你的 Listener 在
之前定义,
就还没创建
正确做法:确保你的 Listener 在
之后(XML 中位置靠下),或监听
(需实现
)
不要在
里硬等 Spring 上下文就绪——没有回调机制,轮询或 sleep 都是反模式
更安全的替代:把初始化逻辑拆进 Spring Bean 的
或
实际顺序问题往往卡在“以为容器懂依赖,其实只认行号”。多一个 Listener 就多一分顺序风险,能合并的逻辑尽量收进同一个 Listener 里。
web.xmlcontextInitialized()orderload-on-startupServletContextServletConfigcontextInitialized()getServletRegistration()ServletContextAttributeListenerStandardContext.startInternalListenerStartException@WebListener@WebListener(order = 1)@WebListenercontextInitialized()ServletContextListenerApplicationContextnullContextLoaderListenerContextLoaderListenerWebApplicationContextContextLoaderListenerContextRefreshedEventApplicationListenercontextInitialized()@PostConstructInitializingBean.afterPropertiesSet()