ZetCode

Spring WebSocket

最后修改于 2023 年 10 月 18 日

Spring WebSocket 教程展示了如何在 Spring Web 应用程序中使用 WebSocket。

Spring 是一个流行的 Java 应用程序框架,用于创建企业级应用程序。

WebSocket

WebSocket 是一种计算机通信协议,通过单个 TCP 连接提供全双工通信通道。WebSocket 用于高度交互的应用程序,例如游戏、聊天或股票市场。

TextWebSocketHandler

Spring 使用 WebSocketHandler 来处理 WebSocket 消息和生命周期事件。TextWebSocketHandlerWebSocketHandler 的一个实现,用于处理文本消息。

Spring TextWebSocketHandler 示例

以下应用程序使用 TextWebSocketHandler 通过 WebSocket 处理文本消息。

web.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           ├───config
│   │           │       MyWebInitializer.java
│   │           │       WebConfig.java
│   │           │       WebSocketConfig.java
│   │           └───handler
│   │                   MyWebSocketHandler.java
│   ├───resources
│   └───webapp
│       │   index.html
│       └───WEB-INF
└───test
    └───java

这是项目结构。

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zetcode</groupId>
    <artifactId>textwebsocketex</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring-version>5.3.23</spring-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>


    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
            </plugin>

            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.49.v20220914</version>
            </plugin>

        </plugins>
    </build>

</project>

pom.xml 文件中,我们有以下依赖项:spring-webmvcjavax.servlet-apispring-websocket

com/zetcode/config/MyWebInitializer.java
package com.zetcode.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MyWebInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class, WebSocketConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

MyWebInitializer 初始化 Spring Web 应用程序。它提供了两个配置类:WebConfigWebSocket

com/zetcode/config/WebConfig.java
package com.zetcode.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan("com.zetcode")
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

WebConfig 配置 DefaultServlet。在我们的应用程序中,我们有一个静态的 index.html 页面,它由 DefaultServlet 处理。

com/zetcode/config/WebSocketConfig.java
package com.zetcode.config;

import com.zetcode.handler.MyWebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private MyWebSocketHandler myWebSocketHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myWebSocketHandler, "/socketHandler");
    }
}

WebSocketConfig 使用 @EnableWebSocket 在 Spring Web 应用程序中配置 WebSocket。

@Autowired
private MyWebSocketHandler myWebSocketHandler;

我们注入我们的 MyWebSocketHandler。它通过 registerWebSocketHandlers 进行注册。

com/zetcode/handler/MyWebSocketHandler.java
package com.zetcode.handler;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.time.LocalTime;

@Component
public class MyWebSocketHandler extends TextWebSocketHandler {

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {

        var clientMessage = message.getPayload();

        if (clientMessage.startsWith("hello") || clientMessage.startsWith("greet")) {
            session.sendMessage(new TextMessage("Hello there!"));
        } else if (clientMessage.startsWith("time")) {
            var currentTime = LocalTime.now();
            session.sendMessage(new TextMessage(currentTime.toString()));
        } else {

            session.sendMessage(new TextMessage("Unknown command"));
        }
    }
}

MyWebSocketHandler 中,我们响应套接字消息。

var clientMessage = message.getPayload();

通过 getPayLoad 方法,我们获取客户端消息。

if (clientMessage.startsWith("hello") || clientMessage.startsWith("greet")) {
    session.sendMessage(new TextMessage("Hello there!"));
} else if (clientMessage.startsWith("time")) {
    var currentTime = LocalTime.now();
    session.sendMessage(new TextMessage(currentTime.toString()));
} else {

    session.sendMessage(new TextMessage("Unknown command"));
}

根据消息,我们将 TextMessage 发送回客户端。

webapp/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.1/semantic.min.css"
            rel="stylesheet">
</head>
<body>

<div class="ui container">

    <h1>Spring MVC 5 WebSocket</h1>

    <div class="two column grid">
        <div class="row">
            <div class="column">
                <label for="myMessage">Message</label>
            </div>

            <div class="column">
                <div class="ui input">
                    <input type="text" id="myMessage">
                </div>
            </div>
        </div>

        <div class="row">
            <div class="column">
                <label for="output">Response from Server</label>
            </div>

            <div class="column">
                <textarea rows="8" cols="50" id="output" readonly="readonly"></textarea>
            </div>
        </div>

        <div class="row">
            <button class="ui button" onclick="send()">Send</button>
        </div>

    </div>
</div>


<script>
    const socketConn = new WebSocket('ws://:8080/socketHandler');

    function send() {
        const clientMsg = document.getElementById('myMessage');

        if (clientMsg.value) {
            socketConn.send(clientMsg.value);
        }
    }

    socketConn.onmessage = (e) => {

        const output = document.getElementById('output');

        output.value += `${e.data}\n`;
    }
</script>
</body>
</html>

index.html 包含应用程序的客户端界面。

const socketConn = new WebSocket('ws://:8080/socketHandler');

在 JavaScript 中,我们创建一个套接字连接。

function send() {
    const clientMsg = document.getElementById('myMessage');

    if (clientMsg.value) {
        socketConn.send(clientMsg.value);
    }
}

点击按钮后,我们使用 send 发送文本消息。

socketConn.onmessage = (e) => {

    const output = document.getElementById('output');

    output.value += `${e.data}\n`;
}

onmessage 事件处理程序在收到响应时被调用。我们获取响应数据并将其添加到文本区域。

在这篇文章中,我们创建了一个支持 WebSocket 的简单 Spring Web 应用程序。

作者

我叫 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教学编程方面拥有超过十年的经验。

列出 所有 Spring 教程