添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
进行设置:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* @author Fuyuanwu
* @date 2019/11/7 16:24
*/
@Configuration
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 将"/hello"路径注册为STOMP端点,这个路径与发送和接收消息的目的路径有所不同,这是一个端点,客户端在订阅或发布消息到目的地址前,要连接该端点,
* 即用户发送请求url="/applicationName/hello"与STOMP server进行连接。之后再转发到订阅url;
* PS:端点的作用——客户端在订阅或发布消息到目的地址前,要连接该端点。
*
* @param stompEndpointRegistry
*/
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
// 在网页上可以通过"/applicationName/hello"来和服务器的WebSocket连接
stompEndpointRegistry.addEndpoint("/hello").setAllowedOrigins("*").withSockJS();
}
/**
* 配置了一个简单的消息代理,如果不重载,默认情况下回自动配置一个简单的内存消息代理,用来处理以"/topic"为前缀的消息。这里重载configureMessageBroker()方法,
* 消息代理将会处理前缀为"/topic"和"/user"的消息。
*
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 服务端向客户端广播/发送消息时允许通过的代理。即,服务端想向客户端发送消息必须要要以 "/topic", "/user" 为前缀,否则消息就发不出去。
registry.enableSimpleBroker("/topic", "/user");
// 表示客户端向服务端 发送/订阅 消息的路径前缀。
registry.setApplicationDestinationPrefixes("/web");
// 服务端向单个客户端(点对点推送)时,服务器推送给客户端的路径前缀。
registry.setUserDestinationPrefix("/user");
}
}
编写用来测试的 controller :
import com.desuo.common.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
/**
* @author Fuyuanwu
* @date 2019/11/7 16:37
*/
@RestController
public class TestController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final SimpMessagingTemplate simpMessagingTemplate;
public TestController(SimpMessagingTemplate simpMessagingTemplate) {
this.simpMessagingTemplate = simpMessagingTemplate;
}
// 测试客户端主动发消息给服务端
@MessageMapping("/message/test")
public String receiveMessage(Principal principal, Book book) {
logger.info("receive message principal name: {}", principal.getName());
logger.info("这是客户端发过来的数据: {}", JsonUtil.writeValueAsString(book));
// 测试:服务端主动发给客户端之广播消息。也可以用 @SendTo("/topic/message") 注解
simpMessagingTemplate.convertAndSend("/topic/message", "这是一条服务端向你广播的消息");
// 测试:服务端主动发给客户端之一对一消息。也可以用 @SendToUser("/topic/message/test") 注解
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/topic/message/test", "这是一条服务端向你单独发送的消息");
// 测试:向客户端的默认路径发送消息默认。默认路径的规则为 "/topic/" + @MessageMapping中的路径,所以这里的路径就是:/topic/message/test
// 根据规则,客户端通过 subscribe 这个路径 /topic/message/test 可以收到return的消息
return "这条是服务端接受到请求后,走默认路径给你返回的消息. receiveMessage";
}
// 测试客户端主动发订阅给服务端
@SubscribeMapping("/subscribe/test")
public String receiveSubscribe(Principal principal) {
logger.info("receive subscriber principal name: {}", principal.getName());
// 根据规则,客户端通过 subscribe 这个路径 /topic/subscribe/test 可以收到return的消息
return "这条是服务端接受到请求后,走默认路径给你返回的消息. receiveSubscribe";
}
public static class Book {
private String title;
private String color;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
}
前端向后端发送和接收消息:
let sock = new SockJS(this.serverUrls.material + '/hello')
let stompClient = this.stompClient = Stomp.over(sock)
stompClient.connect({}, function () {
console.log('connected............')
// 这个会接收到后台广播的消息
stompClient.subscribe('/topic/message', function (message) {
console.log('receive message form subscribe path: /topic/message', message)
})
// 这个会接收到后台点对点发送过来的消息
stompClient.subscribe('/user/topic/message/test', function (message) {
console.log('receive message form subscribe path: /user/topic/message/test', message)
})
// 这个是后台在接收到 前台通过 stompClient.send('/web/message/test'.....) 发出的消息后,后台延默认路径返回的消息。
stompClient.subscribe('/topic/message/test', function (message) {
console.log('receive message form subscribe path: /topic/message/test', message)
})
// 主动发送调阅消息给后台
stompClient.subscribe('/web/subscribe/test', function (message) {
console.log('receive message form subscribe path: /web/subscribe/test', message)
})
// 这个是后台在接收到 前台通过 stompClient.subscribe('/topic/subscribe'.....) 发出的订阅消息后,后台延默认路径返回的消息。
stompClient.subscribe('/topic/subscribe', function (message) {
console.log('receive message form subscribe path: /topic/subscribe', message)
})
// 主动发送消息给后台
stompClient.send('/web/message/test', {}, JSON.stringify({title: 'Thinking in Java', color: 'yellow'}))
})
参考文档:
spring websocket:https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/web.html#websocket-stomp
stomp-js:https://stomp-js.github.io/api-docs/latest/classes/CompatClient.html