You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

关键注意事项

  1. 成员变量共享逻辑:

    • 旧版 WebSocketServlet 是单例模式,成员变量全局共享;但 @ServerEndpoint 默认每个连接实例化一个对象,非静态成员变量仅属于当前连接。
    • 如需全局共享状态,必须使用静态变量、ConcurrentHashMap 等线程安全的全局存储,或依赖注入的单例 Bean(如 Spring 环境下的 @Service)。
  2. 容器兼容性:
    方案二的升级转发逻辑依赖 Servlet 容器的 WebSocket 实现,不同容器(Tomcat、Jetty 等)可能存在细微差异,需测试验证。

内容的提问来源于stack exchange,提问作者robtot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 14:41:44