See more: Spring WebSocket reference 整个例子属于WiseMenuFrameWork 的一部分,可以将整个项目Clone 下来,如果朋友们有需求,我可以整理一个独立的demo出来。
WebSocket是html5带来的一项重大的特性,使得浏览器与服务端之间真正长连接交互成为了可能,这篇文章会带领大家窥探一下Spring 对WebSocket的支持及使用。
基础环境 快速搭建Spring框架,我们使用Spring boot ,这里先不讨论SpringBoot,只知道它是一个“快速搭建Spring项目的一站式解决方案”就OK了。 要使用Spring的WebSocket功能,我们需要添加依赖:
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-websocket</artifactId > </dependency >
这样就轻松开启了WebSocket基础功能。
相关配置 下面我们来配置WebSocket。 首先新增一个WebSocketConfig类,定义全局的配置信息,使用JavaConfig的形式: WebSocketConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints (StompEndpointRegistry registry) { registry.addEndpoint("/socket" ).withSockJS(); } @Override public void configureMessageBroker (MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic" ); registry.setApplicationDestinationPrefixes("/app" ); } }
相关说明:
registerStompEndpoints(StompEndpointRegistry registry)
这个方法的作用是添加一个服务端点,来接收客户端的连接。
registry.addEndpoint("/socket")
表示添加了一个/socket
端点,客户端就可以通过这个端点来进行连接。
withSockJS()
的作用是开启SockJS支持,
configureMessageBroker(MessageBrokerRegistry config)
这个方法的作用是定义消息代理,通俗一点讲就是设置消息连接请求的各种规范信息。
registry.enableSimpleBroker("/topic")
表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息 (比较绕,看完整个例子,大概就能明白了)
registry.setApplicationDestinationPrefixes("/app")
指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀
上面两个方法定义的信息其实是相反的,一个定义了客户端接收的地址前缀,一个定义了客户端发送地址的前缀
到目前为止,整个框架的配置信息已经完成,下面我们来写一个发送公告的例子,展示一下WebSocket的魅力!
编写后台业务 有了上述的基本配置信息,我们就可以编写基本功能了。这里先简单说明两个知识点:
MessageMapping Spring对于WebSocket封装的特别简单,提供了一个@MessageMapping
注解,功能类似@RequestMapping
,它是存在于Controller
中的,定义一个消息的基本请求,功能也跟@RequestMapping
类似,包括支持通配符
的url定义等等,详细用法参见Annotation Message Handling
SimpMessagingTemplate SimpMessagingTemplate
是Spring-WebSocket内置的一个消息发送工具,可以将消息发送到指定的客户端。
下面我们来实现:
新建一个GreetingController
,
1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller public class GreetingController { @Resource private SimpMessagingTemplate simpMessagingTemplate; @RequestMapping ("/helloSocket" ) public String index () { return "/hello/index" ; } @MessageMapping ("/change-notice" ) public void greeting (String value) { this .simpMessagingTemplate.convertAndSend("/topic/notice" , value); } }
相关说明:
index() 指定了一个页面,用来实现WebSocket客户端发送公告功能,使用的是@RequestMapping
,所以接收的是http请求,进行页面跳转。
greeting(String value) 这个方法是接收客户端发送功公告的WebSocket请求,使用的是@MessageMapping
。
this.simpMessagingTemplate.convertAndSend("/topic/notice", value)
这个方法官方给出的解释是Convert the given Object to serialized form, possibly using a MessageConverter, wrap it as a message and send it to the given destination.
意思就是“将给定的对象进行序列化,使用‘MessageConverter’进行包装转化成一条消息,发送到指定的目标”,通俗点讲就是我们使用这个方法进行消息的转发发送!
前面我们全局配置中指定了服务端接收的连接以 app
大头,所以客户端发送公告的请求连接应该是/app/change-notice
。 服务端代码就这么简单,跟写SpringMVC类似,同样上面的geeting(String value)
方法我们还可以使用另一个注解@SendTo
换成另一种写法。
1 2 3 4 5 @MessageMapping ("/change-notice" )@SendTo ("/topic/notice" )public String greeting (String value) { return value; }
相关说明: 改进后的代码更简单,着重理解一下@SendTo
。
@SendTo
定义了消息的目的地。结合例子解释就是“接收/app/change-notice
发来的value,然后将value转发到/topic/notice
客户端。
/topic/notice
是客户端发起连接后,订阅服务端消息时指定的一个地址,用于接收服务端的返回,后面我们在写客户端代码的时候会看见。
到目前为止,服务端代码Coding完毕!下一篇文章我们来写客户端功能。
Read More
See more: Spring WebSocket reference 整个例子属于WiseMenuFrameWork 的一部分,可以将整个项目Clone 下来,如果朋友们有需求,我可以整理一个独立的demo出来。
WebSocket前端准备 前端我们需要用到两个js文件: sockjs.js 和stomp.js
SockJS : SockJS 是一个浏览器上运行的 JavaScript 库,如果浏览器不支持 WebSocket,该库可以模拟对 WebSocket 的支持,实现浏览器和 Web 服务器之间低延迟、全双工、跨域的通讯通道。
Stomp Stomp 提供了客户端和代理之间进行广泛消息传输的框架。Stomp 是一个非常简单而且易用的通讯协议实现,尽管代理端的编写可能非常复杂,但是编写一个 Stomp 客户端却是很简单的事情,另外你可以使用 Telnet 来与你的 Stomp 代理进行交互。
发送公告功能 html代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <div > <div > <button id ="connect" onclick ="connect();" > Connect</button > <button id ="disconnect" disabled ="disabled" onclick ="disconnect();" > Disconnect</button > </div > <div id ="conversationDiv" > <p > <label > notice content?</label > </p > <p > <textarea id ="name" rows ="5" > </textarea > </p > <button id ="sendName" onclick ="sendName();" > Send</button > <p id ="response" > </p > </div > </div >
javascript代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <script src="/js/sockjs-0.3.4.min.js"></script> <script src="/js/stomp.min.js"></script> <script> var stompClient = null ; function setConnected (connected) { document.getElementById('connect' ).disabled = connected; document.getElementById('disconnect' ).disabled = !connected; document.getElementById('conversationDiv' ).style.visibility = connected ? 'visible' : 'hidden' ; document.getElementById('response' ).innerHTML = '' ; } function connect () { var socket = new SockJS('/socket' ); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { setConnected(true ); }); } function disconnect () { if (stompClient != null ) { stompClient.disconnect(); } setConnected(false ); console.log("Disconnected" ); } function sendName () { var value = document.getElementById('name' ).value; stompClient.send("/app/change-notice" , {}, value); } connect(); </script>
相关说明: 关于JavaScript实现WebSocket的功能很简单,基本分以下几步:
开启Socket
var socket = new SockJS('/socket');
先构建一个SockJS对象
stompClient = Stomp.over(socket);
用Stomp将SockJS进行协议封装
stompClient.connect()
与服务端进行连接,同时有一个回调函数,处理连接成功后的操作信息。
发送消息
stompClient.send("/app/change-notice", {}, value);
页面预览:
Paste_Image.png
修改公告功能
当我们发送公告后,将上图的公告信息在不刷新页面的情况下,使用WebSocket将其改变。发送公告的前端代码已经完成,现在我们来写另一个客户端,用来接收第一个页面发送的公告,展示在上图红框中。 功能比较简单,所以下面只给出部分js代码:
1 2 3 4 5 6 7 8 9 10 var noticeSocket = function ( ) { var s = new SockJS('/socket' ); var stompClient = Stomp.over(s); stompClient.connect({}, function ( ) { console .log('notice socket connected!' ); stompClient.subscribe('/topic/notice' , function (data ) { $('.message span.content' ).html(data.body); }); }); };
相关说明: 这里与发送公告代码不同的是,在stompClient
建立连接成功之后,我们要监听服务端发送过来的信息,接收到之后,改变页面上公告的内容,所以用到了stompClient.subscribe()
这个subscribe()
方法功能就是定义一个订阅地址,用来接收服务端的信息(在服务端代码中,我们在@SendTo
中指定了这个订阅地址“/topic/notice”),当收到消息后,在回调函数中处理业务逻辑。
至此,所有的功能开发完毕!
效果演示 首先我们发布一条公告:
然后我们切到另一个页面,发现内容已变:
Read More