第二种稍显麻烦,但是可以添加拦截器在WebSocket连接建立和断开前进行一些额外操作。
不管用哪种实现方式,都需要先导入jar包(如下),其中version根据实际springboot版本选择,避免冲突
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<!-- <version>1.3.5.RELEASE</version> -->
</dependency>
1.1 第一种实现方法
(1)WebSocket 业务逻辑实现。参数传递采用路径参数的方法,通过以下方式获取参数:
@ServerEndpoint("/testWebSocket/{id}/{name}")
public void onOpen(Session session, @PathParam("id") long id, @PathParam("name") String name)
import java.util.concurrent.CopyOnWriteArraySet;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
@ServerEndpoint("/testWebSocket/{id}/{name}")
@RestController
public class TestWebSocket {
// 用来记录当前连接数的变量
private static volatile int onlineCount = 0;
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象
private static CopyOnWriteArraySet<TestWebSocket> webSocketSet = new CopyOnWriteArraySet<TestWebSocket>();
// 与某个客户端的连接会话,需要通过它来与客户端进行数据收发
private Session session;
private static final Logger LOGGER = LoggerFactory.getLogger(TestWebSocket.class);
@OnOpen
public void onOpen(Session session, @PathParam("id") long id, @PathParam("name") String name) throws Exception {
this.session = session;
System.out.println(this.session.getId());
webSocketSet.add(this);
LOGGER.info("Open a websocket. id={}, name={}", id, name);
@OnClose
public void onClose() {
webSocketSet.remove(this);
LOGGER.info("Close a websocket. ");
@OnMessage
public void onMessage(String message, Session session) {
LOGGER.info("Receive a message from client: " + message);
@OnError
public void onError(Session session, Throwable error) {
LOGGER.error("Error while websocket. ", error);
public void sendMessage(String message) throws Exception {
if (this.session.isOpen()) {
this.session.getBasicRemote().sendText("Send a message from server. ");
public static synchronized int getOnlineCount() {
return onlineCount;
public static synchronized void addOnlineCount() {
TestWebSocket.onlineCount++;
public static synchronized void subOnlineCount() {
TestWebSocket.onlineCount--;
(2)配置ServerEndpointExporter,配置后会自动注册所有“@ServerEndpoint”注解声明的Websocket Endpoint
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
1.2 第二种实现方法
(1)WebSocket 业务逻辑实现。参数传递采用类似GET请求的方式传递,服务端的参数在拦截器中获取之后通过attributes传递给WebSocketHandler。
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
@RestController
public class TestWebSocketController implements WebSocketHandler {
private static AtomicInteger onlineCount = new AtomicInteger(0);
private static final ArrayList<WebSocketSession> sessions = new ArrayList<>();
private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketController.class);
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
int onlineNum = addOnlineCount();
LOGGER.info("Oprn a WebSocket. Current connection number: " + onlineNum);
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions.remove(session);
int onlineNum = subOnlineCount();
LOGGER.info("Close a webSocket. Current connection number: " + onlineNum);
@Override
public void handleMessage(WebSocketSession wsSession, WebSocketMessage<?> message) throws Exception {
LOGGER.info("Receive a message from client: " + message.toString());
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
LOGGER.error("Exception occurs on webSocket connection. disconnecting....");
if (session.isOpen()) {
session.close();
sessions.remove(session);
subOnlineCount();
* 是否支持消息拆分发送:如果接收的数据量比较大,最好打开(true), 否则可能会导致接收失败。
* 如果出现WebSocket连接接收一次数据后就自动断开,应检查是否是这里的问题。
@Override
public boolean supportsPartialMessages() {
return true;
public static int getOnlineCount() {
return onlineCount.get();
public static int addOnlineCount() {
return onlineCount.incrementAndGet();
public static int subOnlineCount() {
return onlineCount.decrementAndGet();
(2)HandShake 拦截器实现
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
public class TestHandShakeInterceptor extends HttpSessionHandshakeInterceptor {
private final Logger LOGGER = LoggerFactory.getLogger(TestHandShakeInterceptor.class);
* 在WebSocket连接建立之前的操作,以鉴权为例
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
LOGGER.info("Handle before webSocket connected. ");
// 获取url传递的参数,通过attributes在Interceptor处理结束后传递给WebSocketHandler
// WebSocketHandler可以通过WebSocketSession的getAttributes()方法获取参数
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
String id = serverRequest.getServletRequest().getParameter("id");
String name = serverRequest.getServletRequest().getParameter("name");
if (tokenValidation.validateSign()) {
LOGGER.info("Validation passed. WebSocket connecting.... ");
attributes.put("id", id);
attributes.put("name", name);
return super.beforeHandshake(request, response, wsHandler, attributes);
} else {
LOGGER.error("Validation failed. WebSocket will not connect. ");
return false;
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception ex) {
// 省略
(3)WebSocket 配置类实现(注册WebSocket实现类,绑定接口,同时将实现类和拦截器绑定)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import TestWebSocketController;
import TestHandShakeInterceptor;
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Autowired
private TestWebSocketController testWebSocketController;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(TestWebSocketController, "/testWebSocket")
.addInterceptors(new TestHandShakeInterceptor()).setAllowedOrigins("*");
1.3 补充说明
(1)在WebSocket实现过程中,尤其是通过“@ServerEndpoint”实现的时候,可能会出现注入失败的问题,即注入的Bean为null的问题。可以通过手动注入的方式来解决,需要改造实现类和SpringBoot启动类,如下:
@ServerEndpoint("testWebsocket")
@RestController
public class WebSocketController {
private TestService testService;
private static ApplicationContext applicationContext;
@OnOpen
public void onOpen(Session session) {
testService = applicationContext.getBean(TestService.class);
@OnClose
public void onClose() {}
@OnMessage
public void onMessage(String message, Session session) {}
@OnError
public void onError(Session session, Throwable error) {}
public static void setApplicationContext(ApplicationContext applicationContext) {
WebSocketController.applicationContext = applicationContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import WebSocketController;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// SpringApplication.run(Application.class, args);
SpringApplication springApplication = new SpringApplication(Application.class);
ConfigurableApplicationContext configurableApplicationContext = springApplication.run(args);
WebSocketController.setApplicationContext(configurableApplicationContext); // 解决WebSocket不能注入的问题
2. 客户端的实现,我尝试了html和java WebSocketClient两种方式
2.1 html实现
<!DOCTYPE html>
<title>WebSocket示例</title>
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport' />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
// 不带参数的写法
websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket");
// 通过路径传递参数的方法(服务端采用第一种方法"@ServerEndpoint"实现)
websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket/23/Lebron");
// 通过类似GET请求方式传递参数的方法(服务端采用第二种方法"WebSocketHandler"实现)
websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");
else {
alert('当前浏览器 Not support websocket')
//连接发生错误的回调方法
websocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误");
//连接成功建立的回调方法
websocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功");
//接收到消息的回调方法
websocket.onmessage = function (event) {
setMessageInnerHTML(event.data);
//连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
//发送消息
function send() {
var message = document.getElementById('text').value;
websocket.send(message);
</script>
</html>
2.2 Java WebSocketClient实现
(1)WebSocketClient 实现类
import java.net.URI;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestWebSocketClient extends WebSocketClient {
private final Logger LOGGER = LoggerFactory.getLogger(TestWebSocketClient.class);
public TestWebSocketClient(URI serverUri) {
super(serverUri);
public TestWebSocketClient(URI serverUri, Draft protocolDraft) {
super(serverUri, protocolDraft);
@Override
public void onOpen(ServerHandshake serverHandshake) {
LOGGER.info("Open a WebSocket connection on client. ");
@Override
public void onClose(int arg0, String arg1, boolean arg2) {
LOGGER.info("Close a WebSocket connection on client. ");
@Override
public void onMessage(String msg) {
LOGGER.info("WebSocketClient receives a message: " + msg);
@Override
public void onError(Exception exception) {
LOGGER.error("WebSocketClient exception. ", exception);
(2)WebSocketClient 发送数据
String serverUrl = "ws://127.0.0.1:18080/testWebsocket"
URI recognizeUri = new URI(serverUrl);
client = new TestWebSocketClient(recognizeUri, new Draft_6455());
client.connect();
client.send("This is a message from client. ");