Spring Boot整合WebSocket
我们首先要知道websocket的应用场景:
①在线股票网站
②即时聊天
③多人在线游戏
④应用集群通信
⑤系统性能及时监控
......
下面让我们开始从项目中学习websocket:
(一)首先创建一个spring boot项目,如下图,博主用的是idea:
后续过程不太难,如果还是不太会的话,请看https://www.cnblogs.com/*00/p/11317839.html:
(二)添加依赖:
(三)配置websocket(webscoketmessagebroker.java):
1 package com.example.demo; 2 3 import org.springframework.context.annotation.configuration; 4 import org.springframework.messaging.simp.config.messagebrokerregistry; 5 import org.springframework.web.socket.config.annotation.enablewebsocketmessagebroker; 6 import org.springframework.web.socket.config.annotation.stompendpointregistry; 7 import org.springframework.web.socket.config.annotation.websocketmessagebrokerconfigurer; 8 9 @configuration 10 @enablewebsocketmessagebroker 11 public class webscoketmessagebroker implements websocketmessagebrokerconfigurer { 12 @override 13 public void configuremessagebroker(messagebrokerregistry config){ 14 config.enablesimplebroker("/topic"); 15 config.setapplicationdestinationprefixes("/app"); 16 } 17 @override 18 public void registerstompendpoints(stompendpointregistry registry){ 19 registry.addendpoint("/chat").withsockjs(); 20 } 21 }
讲解时间到:
①自定义类websocketconfig继承自websocketmessagebrokerconfigurer进
行websocket配置,然后通过@enablewebsocketmessagebgroker注解开启
websocket消息代理;
②config.enablesimplebroker("/topic")表示设置消息代理的前缀,即如果消息的
前缀是"/topic",就会将消息转发给消息代理(broker),再由消息代理将消息广
播给当前连接的客户端。
③config.setapplicationdestinationprefixes("/app")表示配置一个或多个前缀,
通过这些前缀过滤出需要备注接方法处理的消息。例如,前缀为“/app”的d-
estination可以通过@messagemapping注解的方法处理,而其他destination
(例如:"/topic" "/queue")将被直接交给broker处理。
④registry.adendpoing("/chat").withsockjs()则表示定义一个前缀为"/chat"的
endpoint,并开启sockjs支持,sockjs可以解决浏览器对websocket的兼容性
问题,客户户端将通过这里配置的url来建立websocket连接。
(四)定义controller(greetingcontroller.java):
1 package org.sang.wschat.controller; 2 3 import org.sang.wschat.model.chat; 4 import org.sang.wschat.model.message; 5 import org.springframework.beans.factory.annotation.autowired; 6 import org.springframework.messaging.handler.annotation.messagemapping; 7 import org.springframework.messaging.handler.annotation.sendto; 8 import org.springframework.messaging.simp.simpmessagingtemplate; 9 import org.springframework.scheduling.annotation.scheduled; 10 import org.springframework.stereotype.controller; 11 12 import java.security.principal; 13 14 @controller 15 public class greetingcontroller { 16 @autowired 17 simpmessagingtemplate messagingtemplate; 18 19 @messagemapping("/hello") 20 @sendto("/topic/greetings") 21 public message greeting(message message) throws exception { 22 return message; 23 } 24 // @messagemapping("/chat") 25 // public void chat(principal principal, chat chat) { 26 // string from = principal.getname(); 27 // chat.setfrom(from); 28 // messagingtemplate.convertandsendtouser(chat.getto(), "/queue/chat", chat); 29 // } 30 }
讲解时间到:
在这一段代码中我们看到@messagemapping("/hello")注解的方法将用来接
收"/app/hello"(上一段代码讲解的第三步)路径发送来的消息,在注解方法
对消息处理后,再将消息转发到@sendto定义的路径上,儿@sendto路径是
一个前缀为"/topic"的路径,所以该消息将被交给消息代理的broker, 之后再
由broker进行广播(上一段代码讲解的第三步)。
快捷聊天界面构建(chat.html):
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>群聊</title> 6 <script src="/webjars/jquery/jquery.min.js"></script> //外部js,添加依赖是有添加 7 <script src="/webjars/sockjs-client/sockjs.min.js"></script> //外部js 8 <script src="/webjars/stomp-websocket/stomp.min.js"></script> //外部js 9 <script src="/app.js"></script> 10 </head> 11 <body> 12 <div> 13 <label for="name">请输入用户名:</label> 14 <input type="text" id="name" placeholder="用户名"> 15 </div> 16 <div> 17 <button id="connect" type="button">连接</button> 18 <button id="disconnect" type="button" disabled="disabled">断开连接</button> 19 </div> 20 <div id="chat" style="display: none;"> 21 <div> 22 <label for="name">请输入聊天内容:</label> 23 <input type="text" id="content" placeholder="聊天内容"> 24 </div> 25 <button id="send" type="button">发送</button> 26 <div id="greetings"> 27 <div id="conversation" style="display: none">群聊进行中...</div> 28 </div> 29 </div> 30 </body> 31 </html>
app.js:
1 var stompclient = null; 2 function setconnected(connected) { 3 $("#connect").prop("disabled", connected); 4 $("#disconnect").prop("disabled", !connected); 5 if (connected) { 6 $("#conversation").show(); 7 $("#chat").show(); 8 } 9 else { 10 $("#conversation").hide(); 11 $("#chat").hide(); 12 } 13 $("#greetings").html(""); 14 } 15 function connect() { 16 if (!$("#name").val()) { 17 return; 18 } 19 // registry.addendpoint("/chat").withsockjs()中的那个"/chat" 20 var socket = new sockjs('/chat'); 21 stompclient = stomp.over(socket); 22 stompclient.connect({}, function (frame) { 23 setconnected(true); 24 // 第一个参数就是目的地地址 25 stompclient.subscribe('/topic/greetings', function (greeting) { 26 showgreeting(json.parse(greeting.body)); 27 }); 28 }); 29 } 30 function disconnect() { 31 if (stompclient !== null) { 32 stompclient.disconnect(); 33 } 34 setconnected(false); 35 } 36 function sendname() { 37 // 发送,第一个参数就是greetingcontroller中的发送源地址 38 stompclient.send("/app/hello", {}, json.stringify({'name': $("#name").val(),'content':$("#content").val()})); 39 } 40 function showgreeting(message) { 41 $("#greetings").append("<div>" + message.name+":"+message.content + "</div>"); 42 } 43 44 $(function () { 45 //分别是点击连接、断开连接、发送三个事件,以及对应出发的函数 46 $( "#connect" ).click(function() { connect(); }); 47 $( "#disconnect" ).click(function() { disconnect(); }); 48 $( "#send" ).click(function() { sendname(); }); 49 });
运行(打开两个页面,进入chat.html,并输入两个用户名,然后点击连接,就可以输入内容了
,输入好就可点击发送了):
(五) 接下来让我们看一下点对点的传送吧!点对点,所以有了用户,因此用到了spring security,故
先要添加spring security的依赖;
(六)配置spring security(websecurityconfig):
1 package org.sang.wschat.config; 2 3 import org.springframework.context.annotation.bean; 4 import org.springframework.context.annotation.configuration; 5 import org.springframework.security.config.annotation.authentication.builders.authenticationmanagerbuilder; 6 import org.springframework.security.config.annotation.web.builders.httpsecurity; 7 import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter; 8 import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder; 9 import org.springframework.security.crypto.password.passwordencoder; 10 11 @configuration 12 public class websecurityconfig extends websecurityconfigureradapter { 13 @bean 14 passwordencoder passwordencoder() { 15 return new bcryptpasswordencoder(); 16 } 17 @override 18 protected void configure(authenticationmanagerbuilder auth) throws exception { 19 auth.inmemoryauthentication() 20 .withuser("admin") 21 .password("$2a$10$rmufxgq5ath4wovkuqyvuecpquseoxzyqilxzbz50dcersga.wyiq") //即123 22 .roles("admin") 23 .and() 24 .withuser("sang") 25 .password("$2a$10$rmufxgq5ath4wovkuqyvuecpquseoxzyqilxzbz50dcersga.wyiq") //即123 26 .roles("user"); 27 } 28 @override 29 protected void configure(httpsecurity http) throws exception { 30 http.authorizerequests() 31 .anyrequest().authenticated() 32 .and() 33 .formlogin().permitall(); 34 } 35 }
(七)改造websocket(websocketconfig.java):
1 package org.sang.wschat.config; 2 3 import org.springframework.context.annotation.configuration; 4 import org.springframework.messaging.simp.config.messagebrokerregistry; 5 import org.springframework.web.socket.config.annotation.enablewebsocketmessagebroker; 6 import org.springframework.web.socket.config.annotation.stompendpointregistry; 7 import org.springframework.web.socket.config.annotation.websocketmessagebrokerconfigurer; 8 9 @configuration 10 @enablewebsocketmessagebroker 11 public class websocketconfig implements websocketmessagebrokerconfigurer { 12 @override 13 public void configuremessagebroker(messagebrokerregistry config) { 14 config.enablesimplebroker("/topic","/queue"); //就这里多加了个"/queue" 15 config.setapplicationdestinationprefixes("/app"); 16 } 17 @override 18 public void registerstompendpoints(stompendpointregistry registry) { 19 registry.addendpoint("/chat").withsockjs(); 20 } 21 }
(八)配置controller:
1 package org.sang.wschat.controller; 2 3 import org.sang.wschat.model.chat; 4 import org.sang.wschat.model.message; 5 import org.springframework.beans.factory.annotation.autowired; 6 import org.springframework.messaging.handler.annotation.messagemapping; 7 import org.springframework.messaging.handler.annotation.sendto; 8 import org.springframework.messaging.simp.simpmessagingtemplate; 9 import org.springframework.scheduling.annotation.scheduled; 10 import org.springframework.stereotype.controller; 11 12 import java.security.principal; 13 14 @controller 15 public class greetingcontroller { 16 @autowired 17 simpmessagingtemplate messagingtemplate; 18 19 @messagemapping("/hello") 20 @sendto("/topic/greetings") 21 public message greeting(message message) throws exception { 22 return message; 23 } 24 @messagemapping("/chat") 25 public void chat(principal principal, chat chat) { 26 string from = principal.getname(); 27 chat.setfrom(from); 28 messagingtemplate.convertandsendtouser(chat.getto(), "/queue/chat", chat); 29 } 30 }
(九)创建聊天界面(类似于chat.html,此处是onlinechat.html):
1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>单聊</title> 6 <script src="/webjars/jquery/jquery.min.js"></script> 7 <script src="/webjars/sockjs-client/sockjs.min.js"></script> 8 <script src="/webjars/stomp-websocket/stomp.min.js"></script> 9 <script src="/chat.js"></script> 10 </head> 11 <body> 12 <div id="chat"> 13 <div id="chatscontent"> 14 </div> 15 <div> 16 请输入聊天内容: 17 <input type="text" id="content" placeholder="聊天内容"> 18 目标用户: 19 <input type="text" id="to" placeholder="目标用户"> 20 <button id="send" type="button">发送</button> 21 </div> 22 </div> 23 </body> 24 </html>
(十)该轮到chat.js了,类似于点对点之前的那个项目的app.js:
1 var stompclient = null; 2 function connect() { 3 var socket = new sockjs('/chat'); 4 stompclient = stomp.over(socket); 5 stompclient.connect({}, function (frame) { 6 stompclient.subscribe('/user/queue/chat', function (chat) { 7 showgreeting(json.parse(chat.body)); 8 }); 9 }); 10 } 11 function sendmsg() { 12 stompclient.send("/app/chat", {}, json.stringify({'content':$("#content").val(), 'to':$("#to").val()})); 13 } 14 function showgreeting(message) { 15 $("#chatscontent").append("<div>" + message.from+":"+message.content + "</div>"); 16 } 17 $(function () { 18 connect(); 19 $( "#send" ).click(function() { sendmsg(); }); 20 });
注:点对点这里要注意,第6行,路径是"/user/queue/chat",因为这个destinationprefix默认值是
"/user",也就是说消息的最终发送路径是"/user/用户名/queue.chat"
结果如下:
add:其实这个里面还有两个mode,一个是message的,另一个是chat的,读者可以下载完整源代码自
行研究,这个项目的完整源代码地址:https://github.com/stray-kite/wschat
下一篇: CSS3实现头像旋转效果
推荐阅读
-
SpringBoot入坑笔记之spring-boot-starter-web 配置文件的使用
-
基于Spring Boot不同的环境使用不同的配置方法
-
Java中Spring WebSocket详解
-
spring和quartz整合,并简单调用(实例讲解)
-
详解spring cloud config整合gitlab搭建分布式的配置中心
-
Spring Boot解决项目启动时初始化资源的方法
-
Spring Boot应用监控的实战教程
-
基于spring boot 1.5.4 集成 jpa+hibernate+jdbcTemplate(详解)
-
Spring Boot利用@Async如何实现异步调用:自定义线程池
-
Spring Boot利用@Async异步调用:使用Future及定义超时详解