Java:如何迁移废弃WebSocketServlet中的doGet/doPost至@ServerEndpoint
迁移方案说明
JSR-356 规范的 @ServerEndpoint 是专门处理 WebSocket 长连接的组件,和传统 HttpServlet 属于完全独立的请求处理体系,无法直接在 @ServerEndpoint 类中添加 doGet/doPost 方法来处理 HTTP 请求。以下是两种可行的迁移方案,同时解决成员变量共享的问题:
方案一:拆分 WebSocket 与 HTTP 请求处理类
将 WebSocket 逻辑和 HTTP 请求逻辑拆分为两个独立的类,通过共享状态容器实现数据交互。
1. WebSocket 端点类(处理长连接)
@ServerEndpoint(value = "/test") public class TestWebSocket { // 全局存储所有活跃的 WebSocket 连接(用于后续推送更新) private static final ConcurrentHashMap<String, TestWebSocket> activeConnections = new ConcurrentHashMap<>(); // 共享的状态变量(对应原代码中的 something) private static String sharedSomething = ""; private Session session; @OnOpen public void onOpen(Session session) { this.session = session; activeConnections.put(session.getId(), this); // 原 createWebSocketInbound 中的逻辑迁移到此处 } @OnMessage public String onMessage(String message, Session session) { // 处理 WebSocket 消息逻辑 return "received: " + message; } @OnClose public void onClose(Session session, CloseReason closeReason) { activeConnections.remove(session.getId()); // 连接关闭逻辑 } // 对外提供修改共享变量的方法,供 HTTP 类调用 public static void updateSharedSomething(String newValue) { sharedSomething = newValue; // 可选:如果需要通知所有在线 WebSocket 连接状态更新 for (TestWebSocket ws : activeConnections.values()) { try { ws.session.getBasicRemote().sendText("Shared value updated to: " + newValue); } catch (IOException e) { e.printStackTrace(); } } } }
2. HTTP Servlet 类(处理 GET/POST 请求)
public class TestHttpServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 修改 WebSocket 类中的共享变量 TestWebSocket.updateSharedSomething("get"); resp.setContentType("text/plain"); resp.getWriter().write("Shared variable updated to 'get'"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String newValue = req.getParameter("something") != null ? req.getParameter("something") : "post"; TestWebSocket.updateSharedSomething(newValue); resp.setContentType("text/plain"); resp.getWriter().write("Shared variable updated to '" + newValue + "'"); } }
方案二:同路径兼容 WebSocket 与 HTTP 请求(进阶)
如果需要在同一个 /test 路径下同时处理 WebSocket 升级请求和普通 HTTP 请求,可以通过 Servlet 的升级机制实现转发:
public class TestCombinedServlet extends HttpServlet { private ServerContainer webSocketContainer; @Override public void init() throws ServletException { // 获取容器中的 WebSocket 服务实例 webSocketContainer = (ServerContainer) getServletContext().getAttribute("javax.websocket.server.ServerContainer"); try { // 注册 WebSocket 端点 webSocketContainer.addEndpoint(new ServerEndpointConfig.Builder(TestWebSocket.class, "/test").build()); } catch (DeploymentException e) { throw new ServletException("Failed to register WebSocket endpoint", e); } } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 判断是否为 WebSocket 升级请求 if ("websocket".equalsIgnoreCase(req.getHeader("Upgrade"))) { // 交给 WebSocket 容器处理 webSocketContainer.doUpgrade(req, resp, null); } else { // 处理普通 HTTP 请求 super.service(req, resp); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { TestWebSocket.updateSharedSomething("get"); resp.getWriter().write("Updated via GET"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String value = req.getParameter("something") != null ? req.getParameter("something") : "post"; TestWebSocket.updateSharedSomething(value); resp.getWriter().write("Updated via POST to: " + value); } }
关键注意事项
成员变量共享逻辑:
- 旧版
WebSocketServlet是单例模式,成员变量全局共享;但@ServerEndpoint默认每个连接实例化一个对象,非静态成员变量仅属于当前连接。 - 如需全局共享状态,必须使用静态变量、
ConcurrentHashMap等线程安全的全局存储,或依赖注入的单例 Bean(如 Spring 环境下的@Service)。
- 旧版
容器兼容性:
方案二的升级转发逻辑依赖 Servlet 容器的 WebSocket 实现,不同容器(Tomcat、Jetty 等)可能存在细微差异,需测试验证。
内容的提问来源于stack exchange,提问作者robtot
相关产品推荐
相关产品推荐

