springboot怎么同时接收multipartfile和json_Spring Boot基于STOMP实战

本文介绍了如何在Spring Boot中基于STOMP协议实现WebSocket通信。阐述了STOMP协议的基本概念、结构以及在Spring Boot中的应用流程。通过实例展示了在Spring Boot中配置STOMP代理、创建实体类、定义消息订阅地址,并提供了前端页面的实现,实现了客户端与服务器之间的消息交互。
摘要由CSDN通过智能技术生成

程序新视界:一个“软实力”、“硬技术”同步成长的平台。

259656488fc67a3570b4a4e12bee7af9.png

在上篇文章中我们学习了WebSocket的基础概念,依旧相关的实战项目。在本篇文章中,我们来学习一下如何基于STOMP协议来进行WebSocket协议的实现。

也就是说基于WebSocket协议有多种实现,基于STOMP来实现是其中的一种,也是Spring Boot推荐的一种。

学习完WebSocket协议,我们知道它并没有规定其消息发送的详细格式。那就意味着每个使用WebSocket的开发者,都需要在服务端和客户端定义一套规则,用来传输信息。而STOMP的出现,正好解决了该问题。

STOMP协议

STOMP:原文Simple Text Orientated Messaging Protocol,是一个简单的文本消息传输协议,属于WebSocket的子协议。

STOMP提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。

STOMP协议并不是为WebSocket所设计的,它其实是消息队列的一种协议,与AMQP,JMS是平级的。只不过由于它的简单性恰巧可以用于定义Websocket的消息体格式。目前很多服务端消息队列都已经支持了STOMP,比如RabbitMQ,Apache ActiveMQ等。

STOMP协议构成

STOMP是基于Text的,也允许传输二进制数据,默认编码是UTF-8。

STOMP是一种基于帧的协议。一帧由一个命令,一组可选的Header和一个可选的Body组成。

COMMAND        -- 注释:命令
header1:value1 -- 注释:一组可选Header之一
header2:value2 -- 注释:一组可选Header之一
-- 注释:一个空行
Body^@ -- 注释:一个可选的Body

上述协议结构中,命令(COMMAND)对应的有SEND、SUBSCRIBE、MESSAGE、CONNECT、CONNECTED等。

Header类似HTTP的Header,有content-length,content-type等。

Body可以是二进制也可以是文本。Body与Header间通过一个空行(EOL)来分隔。

具体实例如下:

SEND
destination:/welcome
content-length:12

{"name":"1"}

STOMP服务端

STOMP服务端可以接收客户端发送的一组目标地址。

地址的格式在协议中并没有具体定义。使用/topic/a、/queue/a、queue-a等格式都是可以的。

这样的好处就是可以通过自定义不同的格式来表示不同的含义。比如,以/topic开头的为发布订阅模式,所有消费端都可以接收到消息;以/user开头的为点对点模式,只会被一个消费者客户端收到。

STOMP客户端

STOMP的客户端可以同时扮演两种角色:消息生产者和消息消费者。

作为生产者时通过SEND帧发送消息到指定的地址。

作为消费者时通过发送SUBSCRIBE帧到已知地址来进行消息订阅,当有生产者发送消息到对应的订阅地址时,作为消费者便会接收到对应的消息。

Spring Boot中的STOMP

首先看STOMP在Spring Boot中的简单流程图:

91a9f6d9203c11cb239d05b4d5c526d0.png

图中各个组件介绍:

  • 生产者客户端(左上组件):发送SEND命令到目的地址(destination)。

  • 消费者客户端(左下组件):订阅地址(destination),并接收此目的地址所推送过来的消息。

  • request channel:一组用来接收生产者型客户端所推送过来的消息的线程池。

  • response channel:一组用来推送消息给消费者型客户端的线程池。

  • broker: 消息队列管理者,也称消息代理。接收客户端的订阅指令,并记录订阅者与目的地址(destination)的关系。

  • SimpAnnotatonMethod:发送到达broker之前,会被该组件拦截,可先处理一些业务逻辑。

  • SimpleBroker:直接转到broker。不会被应用拦截。

整个流程如下:

  • 生产者客户端发送SEND命令消息到指定地址;

  • 服务端request channel接收到消息进行判断;

  • 如果目的地址是应用(/app)目的地址则转到SimpAnnotatonMethod中定义的业务方法进行处理。然后,再转到broker(SimpleBroker)。

  • 如果目的地址是非应用目的地址则直接转到broker。broker构建MESSAGE命令消息, 通过response channel推送给所有订阅对应地址的消费者

上面我们讲解了STOMP的基本原理和在Spring Boot中的处理流程。下面以具体的实例带大家了解如何在Spring Boot中使用STOMP协议。

实例场景

用户访问页面,进入“技术交流群”页面,自动登录会话,可发送消息、离开等操作。同时,服务器端会定时推送“计数”到前端。

实战代码

引入依赖

在Spring Boot项目中引入以下依赖:

<dependencies>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-webartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-websocketartifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-thymeleafartifactId>    dependency>    <dependency>        <groupId>org.projectlombokgroupId>        <artifactId>lombokartifactId>        <optional>trueoptional>    dependency>dependencies>

其中最核心的便是websocket的依赖,上节课中我们演示WebSocket实例时引入的同样是该依赖。

开启STOMP代理配置

通过配置文件来配置STOMP代理以及注册STOMP节点:

package com.secbro.config;import org.springframework.context.annotation.Configuration;import org.springframework.messaging.simp.config.MessageBrokerRegistry;import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;import org.springframework.web.socket.config.annotation.StompEndpointRegistry;import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;/** * * 注解@EnableWebSocketMessageBroker开启使用STOMP协议来传输基于代理的消息 * @author sec * @version 1.0 * @date 2020/2/26 9:06 AM **/@Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig implements WebSocketMessageBrokerConfigurer {  /**   * 注册STOMP协议的节点,并指定映射的URL   */  @Override  public void registerStompEndpoints(StompEndpointRegistry registry) {    // 注册STOMP协议节点    registry.addEndpoint("/simple")        // 解决跨域问题        .setAllowedOrigins("*")        // 指定端点使用SockJS协议        .withSockJS();  }  /**   * 配置消息代理   */  @Override  public void configureMessageBroker(MessageBrokerRegistry registry) {    // 由于是实现推送功能,这里的消息代理是/topic    // 启动简单Broker,消息的发送的地址符合配置的前缀来的消息才发送到这个broker    registry.enableSimpleBroker("/topic");  }}

实体类

创建用于接收消息和返回消息的实体类:

@Data
public class RequestMessage {
private String name;
}
@Data
public class ResponseMessage {
private String message;
}

定义消息订阅地址

下面便是定义具体的消息订阅和发送地址:

@Slf4j@RestControllerpublic class StompController {  private static AtomicInteger index = new AtomicInteger();  @Resource  private SimpMessagingTemplate messagingTemplate;  /**   * 注解@MessageMapping与@RequestMapping类似,定位请求地址;   * 注解@SendTo,指定当服务器有消息需要推送的时候,订阅了@SendTo中路径的客户端发送消息。   */  @MessageMapping("/hello")  @SendTo("/topic/hello")  public ResponseMessage hello(RequestMessage message) {    ResponseMessage resp = new ResponseMessage();    String hello = "welcome," + message.getName() + " !";    log.info("ResponseMessage:{}", hello);    resp.setMessage(hello);    return resp;  }  /**   * 定时推送消息,每隔两秒钟返回一次消息给订阅"/topic/callback"的客户端   */  @Scheduled(fixedRate = 5000)  public void callback() {    // 发送消息    messagingTemplate.convertAndSend("/topic/callback", "index: " + index.getAndIncrement());  }}

其中@MessageMapping("/hello"),与@RequestMapping类似,定位请求地址。注解@SendTo,指定当服务器有消息需要推送的时候,订阅了@SendTo中路径的客户端发送消息。

callback方法通过定时任务向订阅了/topic/callback地址的客户端定时发送消息。

由于开启了定时任务,因此在spring boot启动类上使用了@EnableScheduling注解。

@EnableScheduling@SpringBootApplicationpublic class SpringBootMainApplication {  public static void main(String[] args) {    SpringApplication.run(SpringBootMainApplication.class, args);  }}

前端页面

访问前端页面的Controller如下:

@Controllerpublic class AssistantController {  private AtomicInteger idProducer = new AtomicInteger();  @RequestMapping("/")  public String index(Model model) {    model.addAttribute("username","user" + idProducer.getAndIncrement());    return "index";  }}

主要生成用户名,并返回对应的页面。

前端页面内容如下:

<html lang="en" xmlns:th="http://www.thymeleaf.org"><head>    <meta charset="UTF-8">    <title>基于STOMP模式实现title>    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css">    <script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js">script>    <script src="js/stomp.js">script>    <script src="js/sockjs.min.js">script>head><body class="container" style="width: 70%"><h4>基于STOMP模式实现h4><div class="form-group">    <label for="content">label>    <textarea id="content" class="form-control" readonly="readonly" cols="80" rows="15">textarea>div><div class="form-group">    <label for="message">群发消息  label>    <input id="message" value="" class="form-control"/>    <div style="margin-top: 10px">        <button id="user_exit" class="btn btn-danger">离开button>        <button id="toSend" class="btn btn-info">发送消息button>        <input id="username" th:value="${username}" style="display: none"/>    div>div>body><script type="text/javascript">    $(document).ready(function () {        var stompClient;        var userName = $('#username').val();        // WebSocket的连接地址        var socket = new SockJS('http://localhost:8080/simple');        stompClient = Stomp.over(socket);        stompClient.connect({}, function (frame) {            console.log('Connected:' + frame);            // 客户端订阅消息的目的地址            stompClient.subscribe('/topic/hello', function (response) {                $('#content').append(JSON.parse(response.body).message + '\n');            });            // 客户端消息发送的目的地址,注册定时任务接收            stompClient.subscribe('/topic/callback', function (response) {                $('#content').append(response.body + '\n');            });        });        // 客户端发送消息到服务器        $('#toSend').click(function () {            sendMsg();        });        $(document).keyup(function(event){            // 回车键事件            if(event.keyCode==13){                sendMsg();            }        });        function sendMsg(){            stompClient.send("/hello", {}, JSON.stringify({'name': $('#message').val()}));        }        // 退出        $('#user_exit').click(function () {            if (stompClient != null) {                stompClient.disconnect();            }            $('#content').append('[' + userName + '] 已离开!');            console.log('Disconnected');        });    })script>html>

在该页面中引入了bootstrap样式、jQuery、stomp.js和sockjs.js,其中stomp用来实现stomp协议,sockjs是websocket协议的实现,增加了对浏览器不支持websocket的时候的兼容支持。

整体流程:页面初始化完成,执行js进行stomp的连接,并进行地址的订阅;订阅完成,服务器便会定时发送消息给客户端,客户端可以发送消息给服务器,也可以点击“离开”,断开连接。

实例效果如下: 0cfbd7aeb12637e33e0c11929e5f153f.png

技术视频

目前正在为大家打造一套关于Spring Boot的精品课程:《Spring Boot 2.x全家桶》系列视频教程,该系列视频教程预计20+章节,100+节课,20+小时的内容,涵盖市面上大多数的Spring Boot使用场景。目前录制过半,价格优惠,后期会逐步调整,可点击“阅读原文”,查看相关教程。https://edu.csdn.net/course/detail/20369

92ef9d2519467a325546a6286a8d5849.png

922e2092601201b998f1b9a130423c7c.png

未完待续,持续保持每周更新5-7节课时的节奏中……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值