添加依賴:
<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