Jetty 9中ServletContextListener获取ServerContainer为空的问题
看起来你遇到的问题是在自定义ServletContextListener的contextInitialized方法里获取javax.websocket.server.ServerContainer时返回null,但用Jetty Maven插件运行一切正常,而且已经加了--module=websocket参数。我来帮你分析下可能的原因和解决办法:
可能的原因
- 模块依赖不完整:Jetty Maven插件会自动帮你处理所有Jetty WebSocket相关的依赖,但手动用Java命令运行时,你可能只加了自己的
websocket模块,却漏掉了Jetty本身的WebSocket核心模块(比如org.eclipse.jetty.websocket.server、org.eclipse.jetty.websocket.servlet),这些模块是初始化ServerContainer的关键。 - ServletContext属性设置时机问题:在某些手动启动的场景下,Jetty可能还没把
ServerContainer注入到ServletContext的属性中,你的Listener就先执行了——而Maven插件的启动流程已经帮你协调好了这个时机。
具体解决步骤
1. 确保模块路径和依赖齐全
如果你用的是模块化项目(带有module-info.java),首先要在module-info里声明对Jetty WebSocket模块的依赖:
module your.websocket.module { requires org.eclipse.jetty.websocket.server; requires org.eclipse.jetty.websocket.servlet; requires javax.websocket.api; // 其他必要依赖,比如jetty-server、jetty-servlet等 }
然后运行Java命令时,要把Jetty的相关模块JAR加入模块路径,并且用--add-modules显式包含它们:
java --module-path ./target/classes:./lib/jetty-all-modules --module your.websocket.module/com.your.package.MainClass --add-modules org.eclipse.jetty.websocket.server,org.eclipse.jetty.websocket.servlet
(这里的./lib/jetty-all-modules是存放Jetty相关模块JAR的目录,根据你的实际路径调整)
2. 手动初始化WebSocket容器
如果时机问题导致属性还没被注入,你可以用Jetty提供的WebSocketServletContainerInitializer手动在contextInitialized里初始化容器,这样就能确保ServerContainer被正确设置:
import org.eclipse.jetty.websocket.servlet.WebSocketServletContainerInitializer; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.websocket.server.ServerContainer; public class YourContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); // 手动初始化WebSocket容器,同时配置端点 WebSocketServletContainerInitializer.configure(context, (servletCtx, container) -> { // 可选:配置容器参数,比如最大消息大小 container.setDefaultMaxTextMessageBufferSize(65536); // 注册你的WebSocket端点类 container.addEndpoint(YourWebSocketEndpoint.class); }); // 现在再获取ServerContainer就不会是null了 ServerContainer sc = (ServerContainer) context.getAttribute("javax.websocket.server.ServerContainer"); if (sc != null) { // 执行你需要的操作,比如添加其他端点或配置 } } @Override public void contextDestroyed(ServletContextEvent sce) { // 清理逻辑 } }
3. 验证Jetty服务器配置
如果你是手动启动Jetty服务器(不是用Maven插件),要确保在服务器启动时添加了WebSocket的相关组件。比如:
Server server = new Server(8080); ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); contextHandler.setContextPath("/"); server.setHandler(contextHandler); // 添加WebSocket相关的初始化器 WebSocketServletContainerInitializer.configure(contextHandler.getServletContext(), null); server.start(); server.join();
这样能保证Jetty在启动时就完成WebSocket容器的初始化,ServletContext里的ServerContainer属性也会被正确设置。
为什么Jetty Maven插件正常?
因为Jetty Maven插件内部已经帮你完成了所有依赖的自动导入、WebSocket组件的自动注册,以及启动时机的协调,这些步骤在手动运行时都需要你自己来配置。
内容的提问来源于stack exchange,提问作者buenon

