web.xml中按xml从上到下顺序加载,先于servlet和filter;同一类重复声明会创建多个实例;web.xml配置优先级高于@weblistener注解。

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










