build.gradle文件配置如下
buildscript { ext { springBootVersion = '1.5.9.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' group = 'com' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-data-redis') compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.data:spring-data-redis') compile group: 'com.alibaba', name: 'fastjson', version: '1.1.15' runtime('mysql:mysql-connector-java') compile("org.springframework.boot:spring-boot-starter-websocket") compile("org.webjars:webjars-locator-core") compile("org.webjars:sockjs-client:1.0.2") compile("org.webjars:stomp-websocket:2.3.3") testCompile('org.springframework.boot:spring-boot-starter-test') } 复制代码
服务将接受包含STOMP消息中名称的消息,该消息的主体是JSON对象。如果给出的名称是“Test”,那么该消息可能如下所示:
{ "username ": "Test" } 复制代码
为了建模带有名称的消息,可以使用username 属性和相应的getUsername()方法创建一个普通的旧Java对象:
public class ReceiveBean { private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "UserBeans{" + "username='" + username + '/'' + '}'; } } 复制代码
在收到消息并提取名称后,服务将通过创建问候语并在客户端订阅的单独队列上发布该问候语来处理它。问候语也将是一个JSON对象,可能看起来像这样:
{ "content": "Hello, Test!" } 复制代码
为了建模问候表示,可以添加另一个具有content属性和相应getContent()方法的普通旧Java对象:
public class ServerStatus { private String content; public ServerStatus() {} public ServerStatus (String content) { this.content = content; } public String getContent () { return content; } } 复制代码
Spring将使用Jackson JSON库自动将类型实例编组 Greeting
为JSON。 接下来,创建一个控制器来接收serverStatus消息并发送问候消息。 #####创建消息处理控制器 在Spring使用STOMP消息传递的方法中,STOMP消息可以映射到到 @Controller
类。例如,它 ServerStatusController
被映射为处理消息到目的地“/serverStatus”
@Controller @EnableScheduling public class ServerStatusController { @Autowired private SimpMessagingTemplate simpMessagingTemplate; //当浏览器向服务端发送请求时,通过@MessageMapping映射的地址,类似于@RequestMapping @MessageMapping("/serverStatus") @SendTo("/receive/message") //广播所有用户 //传递的参数会自动的被注入到userbean中 public ServerStatus serverStatus (ReceiveBean receiveBean) throws InterruptedException { // 返回值被广播给所有订户,如@SendTo注释中所指定的"/receive/message" 。请注意,来自输入消息的名称已被清理,因为在这种情况下,它将被回显并重新呈现在客户端的浏览器DOM中。 return new ServerStatus((int) (Math.random() * 10 + 50)); } @Scheduled(fixedRate = 5000) //每个5秒提取一次 @SendTo("/receive/message") //广播所有用户 public Object sendAllMessage () { // 发现消息 simpMessagingTemplate.convertAndSend("/receive/message", new ServerStatus((int) (Math.random() * 10 + 50))); return "callback"; } } 复制代码
配置Spring以启用WebSocket和STOMP消息传递。
/** * 为STOMP消息传递配置Spring * WebSocketConfig被注释@Configuration为表明它是一个Spring配置类。 * @EnableWebSocketMessageBroker: @EnableWebSocketMessageBroker启用WebSocket消息处理,由消息代理支持。 */ @Configuration //@Configuration注解表明它是一个Spring配置类 @EnableWebSocketMessageBroker //通过@EnableWebSocketMessageBroker 注解启用WebSocket消息处理,由消息代理支持。 public class WebsocketConfig implements WebSocketMessageBrokerConfigurer{ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { /* * 该registerStompEndpoints()方法注册“/serverStatus”端点, * 启用SockJS后备选项,以便在WebSocket不可用时可以使用替代传输。 * SockJS客户端将尝试连接到“/serverStatus”并使用可用的最佳传输(websocket,xhr-streaming,xhr-polling等)。 * setAllowedOrigins: 允许跨域 */ registry.addEndpoint("/serverStatus").setAllowedOrigins("*").withSockJS(); } /** * 配置消息代理 * @param registry */ @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/receive"); // 调用enableSimpleBroker()一个简单的基于内存的消息代理,将问候消息带回以“/ receive”为前缀的客户端 // registry.setApplicationDestinationPrefixes("/app"); //为绑定了@MessageMapping注解方法的消息指定“/ app”前缀,该前缀将用于定义所有消息映射 } @Override public void configureWebSocketTransport(WebSocketTransportRegistration registry) { } @Override public void configureClientInboundChannel(ChannelRegistration registration) { } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { } @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { } @Override public boolean configureMessageConverters(List<MessageConverter> messageConverters) { return false; } } 复制代码
SockJS是一个浏览器JavaScript库,提供了一个类似WebSocket的对象。SockJS为您提供了一个连贯的,跨浏览器的JavaScript API,它在浏览器和Web服务器之间创建了一个低延迟,全双工,跨域的通信通道。 在底层SockJS首先尝试使用本地WebSocket。如果失败了,它可以使用各种浏览器特定的传输协议,并通过WebSocket类似的抽象方式呈现它们。
npm install sockjs-client 复制代码
通过在WebSocket 之上使用 STOMP协议,来为浏览器 和 server 间的 通信增加适当的消息语义。(STOMP—— Simple Text Oriented Message Protocol——面向消息的简单文本协议)
npm install @stomp/stompjs websocket --save 复制代码
const SockJS = require('sockjs-client') const Stomp = require('@stomp/stompjs') const socket = new SockJS('http://127.0.0.1:8080/serverStatus') const stompClient = Stomp.over(socket) //创建STOMP客户端 stompClient.connect({}, (frame) => { //连接到服务器 let bean = { username: 'Test' } stompClient.send('/serverStatus', {}, JSON.stringify(bean)) //发送信息 stompClient.subscribe('/receive/message', (receive) => { //订阅并接收消息 console.log('greeting', JSON.parse(receive.body)) }) }) 复制代码
#####效果如图: