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

如何编写兼容Tomcat与WebLogic的Java WebSocket Servlet

跨Tomcat/WebLogic的通用Java WebSocket实现方案

好问题!这确实是企业级Java Web应用跨容器适配时的典型痛点——别担心,Java EE规范早就帮我们解决了这个问题:使用JSR 356(Java API for WebSocket)标准API,Tomcat 7.0.47+、WebLogic 12.1.3+都原生支持这套规范,完全可以写出一套代码兼容两个容器,不需要为每个容器单独实现。

下面我给你拆解具体实现步骤:

1. 依赖配置:只引入标准API,避免容器冲突

首先,确保你的项目只依赖JSR 356的API包(编译期用,运行时由容器提供实现),不要引入任何Tomcat/WebLogic特定的WebSocket实现包。以Maven为例:

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
    <scope>provided</scope>
</dependency>

<scope>provided</scope>很关键,它告诉构建工具不要把这个包打包到WAR里,避免和容器自带的WebSocket实现类冲突。

2. 方式一:注解式端点(最简便,推荐)

JSR 356提供了@ServerEndpoint注解,直接标记一个普通Java类就能成为WebSocket端点,不需要继承任何容器特定的Servlet类。容器会自动识别并适配自身的WebSocket机制。

示例代码:

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;

@ServerEndpoint("/chat")
public class ChatEndpoint {
    // 存储所有在线会话(线程安全)
    private static final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();

    @OnOpen
    public void onOpen(Session session) {
        sessions.add(session);
        System.out.println("新客户端连接:" + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        // 广播消息给所有在线客户端
        for (Session s : sessions) {
            if (s.isOpen()) {
                s.getBasicRemote().sendText("用户[" + session.getId() + "]:" + message);
            }
        }
    }

    @OnClose
    public void onClose(Session session) {
        sessions.remove(session);
        System.out.println("客户端断开连接:" + session.getId());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.err.println("WebSocket错误:" + error.getMessage());
    }
}

这个类在Tomcat和WebLogic上都能直接运行,客户端只需要通过ws://<host>:<port>/<context-path>/chat连接即可。

3. 方式二:编程式端点(适合动态注册场景)

如果需要动态注册WebSocket端点(比如根据配置决定是否开启),可以使用编程式方式,继承JSR 356的Endpoint类:

import javax.websocket.*;
import javax.websocket.server.ServerEndpointConfig;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;

public class ChatProgrammaticEndpoint extends Endpoint {
    private static final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();

    @Override
    public void onOpen(Session session, EndpointConfig config) {
        sessions.add(session);
        System.out.println("新客户端连接:" + session.getId());
        
        // 注册消息处理器
        session.addMessageHandler(new MessageHandler.Whole<String>() {
            @Override
            public void onMessage(String message) {
                try {
                    for (Session s : sessions) {
                        if (s.isOpen()) {
                            s.getBasicRemote().sendText("用户[" + session.getId() + "]:" + message);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void onClose(Session session, CloseReason closeReason) {
        sessions.remove(session);
        System.out.println("客户端断开:" + closeReason.getReasonPhrase());
    }

    @Override
    public void onError(Session session, Throwable throwable) {
        System.err.println("WebSocket错误:" + throwable.getMessage());
    }

    // 注册端点的方法,可在ServletContextListener中调用
    public static void registerEndpoint(ServletContext context) {
        ServerEndpointConfig config = ServerEndpointConfig.Builder.create(
                ChatProgrammaticEndpoint.class, "/chat-programmatic"
        ).build();
        ContainerProvider.getWebSocketContainer().addEndpoint(config);
    }
}

然后在ServletContextListener的contextInitialized方法中调用ChatProgrammaticEndpoint.registerEndpoint(context)完成动态注册,同样兼容两个容器。

4. 容器适配的小细节

  • Tomcat:确保Tomcat版本≥7.0.47(推荐用8.x或9.x),不需要额外配置,WAR包部署后自动识别WebSocket端点。
  • WebLogic:WebLogic 12.1.3及以上版本原生支持JSR 356,如果遇到类加载优先级问题(比如应用的类和WebLogic自带类冲突),可以在weblogic.xml中添加配置:
    <container-descriptor>
        <prefer-application-packages>
            <package-name>javax.websocket.*</package-name>
        </prefer-application-packages>
    </container-descriptor>
    
    不过一般情况下,只要你的WAR包没有打包WebSocket实现类,这个配置不是必须的。

5. 验证兼容性

写一个简单的HTML客户端测试页面,放在项目的webapp目录下:

<!DOCTYPE html>
<html>
<head>
    <title>Chat Test</title>
</head>
<body>
    <input type="text" id="messageInput" placeholder="输入消息"/>
    <button onclick="sendMessage()">发送</button>
    <div id="messageArea"></div>

    <script>
        const ws = new WebSocket('ws://' + window.location.host + window.location.pathname + 'chat');
        ws.onmessage = function(event) {
            const area = document.getElementById('messageArea');
            area.innerHTML += '<p>' + event.data + '</p>';
        };

        function sendMessage() {
            const input = document.getElementById('messageInput');
            ws.send(input.value);
            input.value = '';
        }
    </script>
</body>
</html>

分别在Tomcat和WebLogic上部署项目,访问测试页面,验证消息收发是否正常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:44:15