spring websocket 利用注解接收和发送消息

本文介绍了如何将Spring WebSocket与STOMP协议集成,通过STOMP作为子协议来增强WebSocket的功能,实现消息的发送与订阅。文章详细展示了配置过程及前端与后端的交互示例。

websocket只定义了文字和字节俩种形式的消息格式,没有像http协议那样子有那么丰富的协议规范,我们看看http的协议格式

这里写图片描述

websocket之所以没有自己定义那么多的协议格式,是希望有框架自己来实现定义这些格式,我们称之为websocket的子协议,sub-protocol。

STOMP,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP的消息格式如下:

COMMAND
header1:value1
header2:value2
Body^@

例如发送消息

SEND
destination:/queue/trade
content-type:application/json
content-length:44
{"action":"BUY","ticker":"MMM","shares",44}^@

再看看订阅消息

SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@

然后服务器进行广播消息

MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM
{"ticker":"MMM","price":129.45}^@

详细了解可以看看官网http://stomp.github.io/stomp-specification-1.1.html

spring websocket利用STOMP作为websocket的子协议,原因是stomp可以提供一种类似springmvc的编码方式,可以利用注解进行接收和发送消息以及和springmvc进行无缝的结合。

这样子,我们利用注解来接受和广播websocket信息

@Controller
@RequestMapping("/webSocket")
@MessageMapping("foo")
public class WebSocketController {

@MessageMapping("handle")
@SendTo("/topic/greetings")
public String handle(Task task ,String name) {
     //...
    return "handle2";
}
}

@MessageMapping即可以用来类级别上也可以用在方法级别上,类似springmvc,但是为不同的是,springmvc的路径是类路径/方法路径,中间是“/”,例如/webSocket/xxxx,而websocket是用“.”来分开路径的,类路径.方法路径,例如foo.handle来发送消息到特定路径

@SendTo可以把消息广播到路径上去,例如上面可以把消息广播到”/topic/greetings”,如果客户端在这个路径订阅消息,则可以接收到消息

接下来需要注册下基于stomp子协议的websocket到spring中

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {

        //portfolio-stomp就是websocket的端点,客户端需要注册这个端点进行链接,withSockJS允许客户端利用sockjs进行浏览器兼容性处理
        registry.addEndpoint("/portfolio-stomp").withSockJS(); 

    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");               //设置服务器广播消息的基础路径
        registry.setApplicationDestinationPrefixes("/app");  //设置客户端订阅消息的基础路径
        registry.setPathMatcher(new AntPathMatcher("."));    //可以已“.”来分割路径,看看类级别的@messageMapping和方法级别的@messageMapping
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {

        return true;
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
        // TODO Auto-generated method stub
        registry.addDecoratorFactory(new MyWebSocketHandlerDecoratorFactory());
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
         registration.setInterceptors(new MyChannelInterceptor());
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration registration) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        // TODO Auto-generated method stub

    }

}

前端需要用到sockjs.js和stomp.js

var socket = new SockJS('/whats/portfolio-stomp');

/**
 * 建立成功的回调函数
 */
socket.onopen = function() {
    console.log('open');
};

/**
 * 服务器有消息返回的回调函数
 */
socket.onmessage = function(e) {
    console.log('message', e.data);
};

/**
 * websocket链接关闭的回调函数
 */
socket.onclose = function() {
    console.log('close');
};

var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
    stompClient.subscribe('/topic/greetings',  function(data) { //订阅消息
         $("#ret").text(data);
    });
});

document.getElementById("ws").onclick = function() {
stompClient.send("/app/foo.handle3", {}, JSON.stringify({
        name : "nane",
        taskName : "taskName",
        taskDetail : "taskDetail"
    }));
}

到这里就可以就服务器就可以接收和广播消息了,而客户端就可以发送和订阅消息了。

类中的handle的方法参数绑定接收和格式化的处理类似springmvc中方法参数的处理

而方法参数的返回值会被MessageConverter进行转化封装然后发送给广播出去,类似springmvc的方法中带@responsebody注解时候方法返回值会被httpMessageConverter进行转化一样。

spring websocket的方法参数中还允许有其他一些参数如下

Principal principal ,存放用户的登录验证信息
Message message,最基础的消息体,里面方有header和payload等信息
@Payload 消息体内容
@Header(“..”) 某个头部key的值
@Headers, 所有头部key的map集合
MessageHeaders , SimpMessageHeaderAccessor, MessageHeaderAccessor ,StompHeaderAccessor 消息头信息
@DestinationVariable 类似springmvc中的@PathVariable

在spring websocket中也可以类似springmvc中把变量绑定在路径上,例如

@MessageMapping("bar.{baz}") 
@SendTo("/topic/greetings")
public String handle1(@DestinationVariable String baz) {

     return baz;
 }

这就是注解大概的使用方式,如何已注解方式来进行精准推送和全部推送呢??

<think>我们正在讨论如何在SpringBoot中通过WebSocket接收来自外部系统的消息。根据引用,我们知道在SpringBoot中集成WebSocket的基本步骤:1.添加依赖(spring-boot-starter-websocket等)2.配置WebSocket(通过配置类注册ServerEndpointExporter)3.创建WebSocket端点(使用@ServerEndpoint注解)但是,接收外部系统消息,我们需要一个WebSocket服务器端点来监听连接接收消息。具体实现步骤:1.创建WebSocket服务器端点类,使用@ServerEndpoint注解,并指定端点路径。2.在该类中,我们可以定义回调方法,如@OnOpen(连接建立时调用)、@OnMessage(收到消息时调用)、@OnClose(连接关闭时调用)、@OnError(发生错误时调用)。3.在@OnMessage方法中,我们可以处理接收到的消息。这些消息可以来自任何连接到该端点的客户端,包括外部系统。另外,根据引用[3],我们还可以通过编程方式(使用WebSocketContainer)来创建客户端连接,但这里我们作为服务器端接收消息,主要是创建服务器端点。因此,重点在于如何编写服务器端点。示例代码:首先,确保已经添加了依赖(如引用[1]所示)配置类(如引用[2]所示)。然后,创建一个WebSocket端点:```javaimportjavax.websocket.*;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;@ServerEndpoint("/external")//外部系统连接的端点路径publicclassExternalWebSocketEndpoint{@OnOpenpublicvoidonOpen(Sessionsession){//当有外部系统连接时,可以记录日志或进行身份验证等System.out.println("外部系统连接:"+session.getId());}@OnMessagepublicvoidonMessage(Stringmessage,Sessionsession){//当接收到外部系统发送消息时System.out.println("收到来自外部系统的消息:"+message);//这里可以处理消息,比如解析、存储、转发等//如果需要回复,可以使用session.getBasicRemote().sendText()发送消息}@OnClosepublicvoidonClose(Sessionsession){System.out.println("外部系统断开连接:"+session.getId());}@OnErrorpublicvoidonError(Sessionsession,Throwableerror){System.err.println("发生错误:"+session.getId());error.printStackTrace();}}```注意:上面的端点类需要被Spring管理(即放在SpringBoot扫描的包下),因为引用[2]中的配置类`ServerEndpointExporter`会扫描带有`@ServerEndpoint`注解的Bean并注册。这样,外部系统就可以通过WebSocket客户端连接到这个端点(例如:ws://your-server:port/external)并发送消息。关于外部系统如何连接:外部系统可以是任何支持WebSocket的客户端(如JavaScript、Java客户端等)。只要知道我们暴露的端点URL,它们就可以发送消息。此外,引用[3]中提到了使用`WebSocketContainer`来创建客户端连接,但这里我们作为服务器接收消息,所以不需要客户端代码。不过,如果我们的系统也需要作为客户端连接其他WebSocket服务器,那么可以使用引用[3]中的方式。另外,引用[4][5]提到了定时推送,但这不是接收外部消息所必需的,所以我们可以忽略。安全考虑:在实际应用中,我们可能需要对外部系统进行身份验证,可以在连接建立时(@OnOpen)通过查询参数或HTTP头信息(WebSocket握手时可以带参数)进行验证。总结:在SpringBoot中接收外部系统WebSocket消息的关键是创建一个使用`@ServerEndpoint`注解的端点类,并在其中处理`@OnMessage`事件。相关问题:1.如何确保WebSocket连接的安全性(如身份验证)?2.如何处理高并发下的WebSocket连接?3.WebSocket消息的格式如何设计(如JSON、二进制等)?</think>### Spring Boot WebSocket 接收外部系统消息实现方式 在Spring Boot中接收外部系统通过WebSocket发送消息,核心是创建**WebSocket服务器端点**处理连接消息。以下是关键实现步骤: #### 1. 添加依赖 ```gradle dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'org.webjars:sockjs-client:1.5.1' implementation 'org.webjars:stomp-websocket:2.3.4' } ``` #### 2. 配置WebSocket端点 ```java 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(); // 自动注册@ServerEndpoint注解类 } } ``` #### 3. 创建消息处理端点 ```java import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; @ServerEndpoint("/external-message") // 外部系统连接地址 @Component public class ExternalMessageEndpoint { private static final CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>(); @OnOpen public void onOpen(Session session) { sessions.add(session); System.out.println("外部系统连接: " + session.getId()); } @OnMessage public void onMessage(String message, Session session) { System.out.println("收到外部消息: " + 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("连接错误: " + session.getId()); error.printStackTrace(); } // 主动发送消息给外部系统(可选) public static void sendToExternal(String message) throws IOException { for (Session session : sessions) { if (session.isOpen()) { session.getBasicRemote().sendText(message); } } } } ``` #### 4. 外部系统连接方式 外部系统可通过标准WebSocket客户端连接: ```javascript // JavaScript示例 const socket = new WebSocket('ws://your-server:port/external-message'); socket.onmessage = (event) => { console.log('收到服务端响应:', event.data) }; socket.send('外部系统发送消息'); ``` #### 关键说明: 1. **消息接收原理** 外部系统连接到`ws://your-server:port/external-message`后,通过`@OnMessage`注解方法自动接收消息。 2. **消息格式处理** - 文本消息:直接使用`String`类型参数 - 二进制消息:使用`ByteBuffer`参数 - JSON数据:结合Jackson解析`ObjectMapper().readValue(message, DTO.class)` 3. **安全性增强**(可选) ```java @OnOpen public void onOpen(Session session, @PathParam("token") String token) { if (!validateToken(token)) { // 验证连接令牌 session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "无效凭证")); } } ``` 外部系统连接时需携带令牌:`ws://.../external-message?token=xxx` 4. **性能优化** - 使用`@Async`处理耗时操作 - 消息压缩:`session.getBasicRemote().setBatchingSize(2048)` - 连接数限制:通过Nginx配置最大连接数 > **最佳实践**:对于需要消息路由的场景(如STOMP协议),可结合`@MessageMapping`注解实现,但简单消息接收使用原生API更轻量[^1][^3]。 --- ### 相关问题 1. 如何处理WebSocket消息的并发线程安全问题? 2. WebSocket与HTTP长轮询在实时消息场景下的性能对比? 3. 如何实现WebSocket连接的负载均衡故障转移? 4. WebSocket消息传输中如何保证数据的一致性可靠性? 5. 在微服务架构中,如何通过WebSocket实现跨服务消息广播? [^1]: Spring Boot中的WebSocket技术实现 [^2]: webSocket介绍与使用 spring boot 集成 websocket教程 java [^3]: WebSocket消息发送方式 [^4]: Spring Boot + WebSocket 实时消息推送系统 [^5]: 基于注解WebSocket实现方案
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值