引入依赖
pom依赖
和上一篇博客一样,spring-boot-starter-websocket才是关键的依赖,并且本篇博客主要讲的是STOMP协议的实现。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
配置
配置类
主要是注册STOMP节点,并配置订阅消息的路径前缀。
package com.secbro.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;
/**
*
* 注解@EnableWebSocketMessageBroker开启使用STOMP协议来传输基于代理的消息
* @author sec
* @version 1.0
* @date 2020/2/26 9:06 AM
**/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 注册STOMP协议的节点,并指定映射的URL
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 注册STOMP协议节点
registry.addEndpoint("/simple")
// 解决跨域问题
.setAllowedOrigins("*")
// 指定端点使用SockJS协议
.withSockJS();
}
/**
* 配置消息代理
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 由于是实现推送功能,这里的消息代理是/topic
// 启动简单Broker,消息的发送的地址符合配置的前缀来的消息才发送到这个broker
registry.enableSimpleBroker("/topic");
}
}
实体类
接受返回消息的实体类
@Data
public class RequestMessage {
private String name;
}
@Data
public class ResponseMessage {
private String message;
}
Controller和启动类
Controller
@MessageMapping("/hello"),前端发送消息的路径;而@SendTo("/topic/hello")是接收到消息后发送给订阅/topic/hello的用户。关于订阅的配置,上述配置类的registry.enableSimpleBroker("/topic")已经设置了订阅路径的前缀,没有这个订阅该路径不会被实现。
@Slf4j
@RestController
public class StompController {
private static AtomicInteger index = new AtomicInteger();
@Resource
private SimpMessagingTemplate messagingTemplate;
/**
* 注解@MessageMapping与@RequestMapping类似,定位请求地址;
* 注解@SendTo,指定当服务器有消息需要推送的时候,订阅了@SendTo中路径的客户端发送消息。
*/
@MessageMapping("/hello")
@SendTo("/topic/hello")
public ResponseMessage hello(RequestMessage message) {
ResponseMessage resp = new ResponseMessage();
String hello = "welcome," + message.getName() + " !";
log.info("ResponseMessage:{}", hello);
resp.setMessage(hello);
return resp;
}
/**
* 定时推送消息,每隔两秒钟返回一次消息给订阅"/topic/callback"的客户端
*/
@Scheduled(fixedRate = 5000)
public void callback() {
// 发送消息
messagingTemplate.convertAndSend("/topic/callback", "index: " + index.getAndIncrement());
}
}
启动类
因为上述Controller开启了定时任务,所以启动类注解了@EnableScheduling
@EnableScheduling
@SpringBootApplication
public class SpringBootMainApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMainApplication.class, args);
}
}
JS代码
前端JS
stomp.js是为了支持STOMP协议;而sockjs.min.js是为了创建STOMP的对象,其中封装了websocket协议。
不像上一篇博客,new一个websocket对象(ws://xxx),new SockJS('http://localhost:8080/simple')才能正确连接上后端声明的STOMP节点,/simple已经在上述配置类声明了。之后就可以创建客户端对象(Stomp.over(socket)),根据该对象的订阅和发送方法,进行websocket长连接。
<script src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
<script src="js/stomp.js"></script>
<script src="js/sockjs.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var stompClient;
var userName = $('#username').val();
// WebSocket的连接地址
var socket = new SockJS('http://localhost:8080/simple');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected:' + frame);
// 客户端订阅消息的目的地址
stompClient.subscribe('/topic/hello', function (response) {
$('#content').append(JSON.parse(response.body).message + '\n');
});
// 客户端消息发送的目的地址,注册定时任务接收
stompClient.subscribe('/topic/callback', function (response) {
$('#content').append(response.body + '\n');
});
});
// 客户端发送消息到服务器
$('#toSend').click(function () {
sendMsg();
});
$(document).keyup(function(event){
// 回车键事件
if(event.keyCode==13){
sendMsg();
}
});
function sendMsg(){
stompClient.send("/hello", {}, JSON.stringify({'name': $('#message').val()}));
}
// 退出
$('#user_exit').click(function () {
if (stompClient != null) {
stompClient.disconnect();
}
$('#content').append('[' + userName + '] 已离开!');
console.log('Disconnected');
});
})
</script>
评论区