websocket通常用于即时通讯场景中, 在SpringBoot下也提供了jar支持spring-boot-starter-websocket
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>websocket</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.1-1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebsocketApplication {
public static void main(String[] args) {
SpringApplication.run(WebsocketApplication.class);
}
}
配置类
package com.websocket.demo.im.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/wechat").withSockJS();
}
}
Controller
通过mid和fid来确定一个聊天对象
package com.websocket.demo.im.controller;
import com.websocket.demo.im.dto.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@Controller
public class IndexController {
@GetMapping("/")
public String index(
@RequestParam(value = "mid", defaultValue = "GuanDS") String mid,
@RequestParam(value = "fid", defaultValue = "admin") String fid,
Model model) {
model.addAttribute("fid", fid);
model.addAttribute("mid", mid);
return "wechat";
}
@ResponseBody
@SendTo("/topic/{mid}_{fid}")
@MessageMapping("/send/{mid}_{fid}")
public Object greeting(Message message) {
log.info("Send msg: {} {}", message.getRecipientId(), message.getContent());
return message;
}
}
wechat.ftl
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title></title>
<link href="/static/css/mui.min.css" rel="stylesheet" />
<style>
html,
body {
height: 100%;
margin: 0px;
padding: 0px;
overflow: hidden;
-webkit-touch-callout: none;
-webkit-user-select: none;
}
footer {
position: fixed;
width: 100%;
height: 50px;
min-height: 50px;
border-top: solid 1px #bbb;
left: 0px;
bottom: 0px;
overflow: hidden;
padding: 0px 50px;
background-color: #fafafa;
}
.footer-left {
position: absolute;
width: 50px;
height: 50px;
left: 0px;
bottom: 0px;
text-align: center;
vertical-align: middle;
line-height: 100%;
padding: 12px 4px;
}
.footer-right {
position: absolute;
width: 50px;
height: 50px;
right: 0px;
bottom: 0px;
text-align: center;
vertical-align: middle;
line-height: 100%;
padding: 12px 5px;
display: inline-block;
}
.footer-center {
height: 100%;
padding: 5px 0px;
}
.footer-center [class*=input] {
width: 100%;
height: 100%;
border-radius: 5px;
}
.footer-center .input-text {
background: #fff;
border: solid 1px #ddd;
padding: 10px !important;
font-size: 16px !important;
line-height: 18px !important;
font-family: verdana !important;
overflow: hidden;
}
.footer-center .input-sound {
background-color: #eee;
}
.mui-content {
height: 100%;
padding: 44px 0px 50px 0px;
overflow: auto;
background-color: #eaeaea;
}
#msg-list {
height: 100%;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.msg-item {
padding: 8px;
clear: both;
}
.msg-item .msg-content {
display: inline-block;
border-radius: 5px;
border: solid 1px #d3d3d3;
background-color: #FFFFFF;
color: #333;
padding: 8px;
vertical-align: top;
font-size: 15px;
position: relative;
margin: 0px 8px;
max-width: 75%;
min-width: 35px;
float: left;
}
.msg-item .msg-content .msg-content-inner {
overflow-x: hidden;
}
.msg-item .msg-content .msg-content-arrow {
position: absolute;
border: solid 1px #d3d3d3;
border-right: none;
border-top: none;
background-color: #FFFFFF;
width: 10px;
height: 10px;
left: -5px;
top: 12px;
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg);
}
.msg-item-self .msg-user,
.msg-item-self .msg-content {
float: right;
}
.msg-item-self .msg-content .msg-content-arrow {
left: auto;
right: -5px;
-webkit-transform: rotateZ(225deg);
transform: rotateZ(225deg);
}
.msg-item-self .msg-content,
.msg-item-self .msg-content .msg-content-arrow {
background-color: #4CD964;
color: #fff;
border-color: #2AC845;
}
footer .mui-icon {
color: #000;
}
footer .mui-icon:active {
color: #007AFF !important;
}
#msg-sound {
-webkit-user-select: none !important;
user-select: none !important;
}
.rprogress {
position: absolute;
left: 50%;
top: 50%;
width: 140px;
height: 140px;
margin-left: -70px;
margin-top: -70px;
background-image: url(../images/arecord.png);
background-repeat: no-repeat;
background-position: center center;
background-size: 30px 30px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 5px;
display: none;
-webkit-transition: .15s;
}
.rschedule {
background-color: rgba(0, 0, 0, 0);
border: 5px solid rgba(0, 183, 229, 0.9);
opacity: .9;
border-left: 5px solid rgba(0, 0, 0, 0);
border-right: 5px solid rgba(0, 0, 0, 0);
border-radius: 50px;
box-shadow: 0 0 15px #2187e7;
width: 46px;
height: 46px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -23px;
margin-top: -23px;
-webkit-animation: spin 1s infinite linear;
animation: spin 1s infinite linear;
}
.r-sigh{
display: none;
border-radius: 50px;
box-shadow: 0 0 15px #2187e7;
width: 46px;
height: 46px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -23px;
margin-top: -23px;
text-align: center;
line-height: 46px;
font-size: 40px;
font-weight: bold;
color: #2187e7;
}
.rprogress-sigh{
background-image: none !important;
}
.rprogress-sigh .rschedule{
display: none !important;
}
.rprogress-sigh .r-sigh{
display: block !important;
}
.rsalert {
font-size: 12px;
color: #bbb;
text-align: center;
position: absolute;
border-radius: 5px;
width: 130px;
margin: 5px 5px;
padding: 5px;
left: 0px;
bottom: 0px;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#h {
background: #fff;
border: solid 1px #ddd;
padding: 10px !important;
font-size: 16px !important;
font-family: verdana !important;
line-height: 18px !important;
overflow: visible;
position: absolute;
left: -1000px;
right: 0px;
word-break: break-all;
word-wrap: break-word;
}
.cancel {
background-color: darkred;
}
</style>
</head>
<body>
<header class="mui-bar mui-bar-nav">
<h1 class="mui-title">与 ${fid!} 聊天中...</h1>
</header>
<pre id='h'></pre>
<div class="mui-content">
<div id='msg-list'>
</div>
</div>
<footer>
<div class="footer-left">
<i id='msg-image' class="mui-icon mui-icon-camera" style="font-size: 28px;"></i>
</div>
<div class="footer-center">
<textarea id='msg-text' type="text" class='input-text'></textarea>
</div>
<label for="" class="footer-right">
<i id='msg-send' class="mui-icon mui-icon mui-icon-paperplane"></i>
</label>
</footer>
<script src="/webjars/jquery/3.1.1-1/jquery.min.js"></script>
<script src="/webjars/sockjs-client/1.0.2/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/2.3.3/stomp.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = new SockJS('/wechat');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/${mid!}_${fid!}', function (greeting) {
$('#msg-list').append(sendContent(false, JSON.parse(greeting.body).content));
});
});
function sendContent(self, content) {
return "<div class=\"msg-item " + (self ? "msg-item-self" : "") + "\"><i class=\"msg-user mui-icon mui-icon-person\"></i>" +
"<div class=\"msg-content\"><div class=\"msg-content-inner\">" + content +
"</div><div class=\"msg-content-arrow\"></div></div></div>";
}
$('#msg-send').on('click', function () {
var msg = $("#msg-text").val();
if (msg === '') {
return;
}
stompClient.send("/app/send/${fid!}_${mid!}", {}, JSON.stringify({'content': msg}));
$('#msg-list').append(sendContent(true, msg));
$("#msg-text").val('');
})
</script>
</body>
</html>
运行结果
此时只是简单实现了聊天的功能, 但是没有太完善, 只做参考…
参考源码 https://github.com/spring-guides/gs-messaging-stomp-websocket