Spring Boot中WebSocket常用使用方法詳解
在實(shí)時(shí)性要求較高的應(yīng)用場景,如在線聊天、實(shí)時(shí)數(shù)據(jù)監(jiān)控、股票行情推送等,傳統(tǒng)的HTTP協(xié)議由于其請(qǐng)求-響應(yīng)的模式,無法高效實(shí)現(xiàn)服務(wù)器與客戶端之間的雙向?qū)崟r(shí)通信。而WebSocket協(xié)議的出現(xiàn)解決了這一難題,它允許在單個(gè)TCP連接上進(jìn)行全雙工通信,使得服務(wù)器和客戶端可以隨時(shí)主動(dòng)發(fā)送消息。Spring Boot對(duì)WebSocket提供了良好的支持,極大地簡化了開發(fā)流程。本文將從入門到精通,詳細(xì)介紹Spring Boot中WebSocket的常用使用方法。
一、WebSocket基礎(chǔ)概念
1.1 什么是WebSocket
WebSocket是一種網(wǎng)絡(luò)通信協(xié)議,于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并被HTML5所支持 。與HTTP協(xié)議不同,WebSocket在建立連接后,通信雙方可以隨時(shí)主動(dòng)發(fā)送和接收數(shù)據(jù),無需像HTTP那樣每次通信都要建立新的連接,從而減少了開銷,提高了實(shí)時(shí)性。
1.2 WebSocket與HTTP的區(qū)別
| 特性 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 客戶端發(fā)起請(qǐng)求,服務(wù)器響應(yīng)(單向) | 全雙工通信(雙向) |
| 連接方式 | 每次請(qǐng)求都需建立新連接 | 一次握手建立持久連接 |
| 數(shù)據(jù)格式 | 通常為文本(JSON、XML等) | 支持文本和二進(jìn)制數(shù)據(jù) |
| 應(yīng)用場景 | 適用于一般的Web頁面請(qǐng)求 | 適用于實(shí)時(shí)性要求高的場景 |
二、Spring Boot集成WebSocket
2.1 添加依賴
在Spring Boot項(xiàng)目的pom.xml文件中添加WebSocket依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>如果使用Gradle,在build.gradle中添加:
implementation 'org.springframework.boot:spring-boot-starter-websocket'
2.2 配置WebSocket
創(chuàng)建一個(gè)配置類,用于注冊(cè)WebSocket處理程序和配置消息代理:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(StompBrokerRelayRegistration config) {
config.setApplicationDestinationPrefixes("/app");
config.setDestinationPrefixes("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket-endpoint").withSockJS();
}
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(65536);
container.setMaxBinaryMessageBufferSize(65536);
return container;
}
}在上述代碼中:
@EnableWebSocketMessageBroker注解啟用WebSocket消息代理。configureMessageBroker方法配置消息代理的前綴,/app用于應(yīng)用程序發(fā)送消息的目的地前綴,/topic用于服務(wù)器發(fā)送消息的目的地前綴。registerStompEndpoints方法注冊(cè)WebSocket端點(diǎn),addEndpoint方法指定端點(diǎn)的路徑,withSockJS表示啟用SockJS支持,以提供對(duì)不支持WebSocket瀏覽器的兼容。createWebSocketContainer方法配置WebSocket容器的參數(shù),如消息緩沖區(qū)大小。
三、WebSocket常用使用方法
3.1 簡單消息收發(fā)
3.1.1 創(chuàng)建消息實(shí)體類
public class ChatMessage {
private String sender;
private String content;
private MessageType type;
// 省略構(gòu)造函數(shù)、Getter和Setter方法
public enum MessageType {
CHAT, JOIN, LEAVE
}
}ChatMessage類用于封裝聊天消息,包含發(fā)送者、消息內(nèi)容和消息類型(聊天、加入、離開)。
3.1.2 創(chuàng)建消息處理類
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/chat.send")
@SendTo("/topic/public")
public ChatMessage sendMessage(ChatMessage chatMessage) {
return chatMessage;
}
@MessageMapping("/chat.join")
@SendTo("/topic/public")
public ChatMessage joinChat(ChatMessage chatMessage) {
chatMessage.setType(ChatMessage.MessageType.JOIN);
return chatMessage;
}
}在上述代碼中:
@MessageMapping注解用于映射客戶端發(fā)送的消息路徑,如"/chat.send"和"/chat.join"。@SendTo注解指定消息發(fā)送的目的地,這里將消息發(fā)送到"/topic/public",所有訂閱該主題的客戶端都能接收到消息。sendMessage方法處理聊天消息的發(fā)送,joinChat方法處理用戶加入聊天的消息。
3.1.3 前端頁面實(shí)現(xiàn)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.5.1/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stompjs/2.3.3/stomp.min.js"></script>
</head>
<body>
<input type="text" id="username" placeholder="用戶名">
<button onclick="connect()">連接</button>
<div id="chat-window"></div>
<input type="text" id="message" placeholder="輸入消息">
<button onclick="sendMessage()">發(fā)送</button>
<script>
let socket = new SockJS('/websocket-endpoint');
let stompClient = Stomp.over(socket);
function connect() {
let username = document.getElementById('username').value;
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/public', function (message) {
let chatWindow = document.getElementById('chat-window');
let msg = JSON.parse(message.body);
if (msg.type === 'JOIN') {
chatWindow.innerHTML += msg.sender + " 加入了聊天
";
} else {
chatWindow.innerHTML += msg.sender + ": " + msg.content + "
";
}
});
let joinMessage = {
sender: username,
content: '',
type: 'JOIN'
};
stompClient.send("/app/chat.join", {}, JSON.stringify(joinMessage));
});
}
function sendMessage() {
let message = document.getElementById('message').value;
let username = document.getElementById('username').value;
let chatMessage = {
sender: username,
content: message,
type: 'CHAT'
};
stompClient.send("/app/chat.send", {}, JSON.stringify(chatMessage));
}
</script>
</body>
</html>前端頁面通過SockJS和StompJS庫與后端建立WebSocket連接,實(shí)現(xiàn)消息的發(fā)送和接收。
3.2 點(diǎn)對(duì)點(diǎn)消息發(fā)送
有時(shí)候需要實(shí)現(xiàn)一對(duì)一的消息發(fā)送,而不是廣播給所有客戶端。可以通過在@SendTo中指定具體的用戶目的地來實(shí)現(xiàn)。
3.2.1 配置用戶目的地前綴
在WebSocketConfig類中添加用戶目的地前綴配置:
@Override
public void configureMessageBroker(StompBrokerRelayRegistration config) {
config.setApplicationDestinationPrefixes("/app");
config.setDestinationPrefixes("/topic", "/user");
config.setUserDestinationPrefix("/user");
}這里添加了/user作為用戶目的地前綴。
3.2.2 修改消息處理類
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
@Controller
public class PrivateChatController {
@MessageMapping("/chat.private")
public void sendPrivateMessage(SimpMessageHeaderAccessor headerAccessor, ChatMessage chatMessage) {
String recipient = chatMessage.getRecipient();
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
this.stompMessagingTemplate.convertAndSendToUser(recipient, "/private", chatMessage);
}
}在上述代碼中:
@MessageMapping("/chat.private")映射處理點(diǎn)對(duì)點(diǎn)消息的路徑。SimpMessageHeaderAccessor用于獲取和設(shè)置消息頭信息。stompMessagingTemplate.convertAndSendToUser方法將消息發(fā)送到指定用戶的私有目的地。
3.2.3 前端實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)消息發(fā)送
function sendPrivateMessage() {
let message = document.getElementById('message').value;
let username = document.getElementById('username').value;
let recipient = document.getElementById('recipient').value;
let chatMessage = {
sender: username,
recipient: recipient,
content: message,
type: 'CHAT'
};
stompClient.send("/app/chat.private", {}, JSON.stringify(chatMessage));
}前端添加輸入接收者的文本框,并在發(fā)送消息時(shí)指定接收者,實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)消息發(fā)送。
3.3 消息攔截與認(rèn)證
在實(shí)際應(yīng)用中,可能需要對(duì)WebSocket消息進(jìn)行攔截和認(rèn)證,確保只有合法用戶才能進(jìn)行通信。
3.3.1 創(chuàng)建消息攔截器
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
@Component
public class WebSocketInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// 在這里進(jìn)行認(rèn)證邏輯,如檢查Token等
String token = accessor.getFirstNativeHeader("Authorization");
if (token == null ||!isValidToken(token)) {
throw new RuntimeException("認(rèn)證失敗");
}
}
return message;
}
private boolean isValidToken(String token) {
// 實(shí)現(xiàn)具體的Token驗(yàn)證邏輯
return true;
}
}上述代碼創(chuàng)建了一個(gè)WebSocketInterceptor攔截器,在preSend方法中對(duì)連接請(qǐng)求進(jìn)行認(rèn)證,檢查請(qǐng)求頭中的Authorization Token是否有效。
3.3.2 注冊(cè)攔截器
在WebSocketConfig類中注冊(cè)攔截器:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new WebSocketInterceptor());
}
// 其他配置方法...
}通過configureClientInboundChannel方法將攔截器注冊(cè)到客戶端入站通道,對(duì)所有進(jìn)入的消息進(jìn)行攔截處理。
四、不使用接口,基于注解的WebSocket實(shí)現(xiàn)
4.1 實(shí)現(xiàn)思路
在Spring Boot中,除了通過實(shí)現(xiàn)接口的方式處理WebSocket消息,還可以利用注解來簡化開發(fā)過程。通過@ServerEndpoint注解定義WebSocket端點(diǎn),結(jié)合@OnOpen、@OnMessage、@OnClose、@OnError等注解,能夠輕松實(shí)現(xiàn)對(duì)WebSocket連接生命周期的監(jiān)聽,以及接收和處理客戶端發(fā)送的數(shù)據(jù)。
4.2 核心代碼實(shí)現(xiàn)
首先,創(chuàng)建一個(gè)WebSocket處理類:
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/ws/{userId}")
public class MyWebSocket {
// 靜態(tài)變量,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的。
private static int onlineCount = 0;
// concurrent包的線程安全Set,用來存放每個(gè)客戶端對(duì)應(yīng)的MyWebSocket對(duì)象。
private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<>();
// 與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
private Session session;
// 接收userId
private String userId;
/**
* 連接建立成功調(diào)用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
webSocketSet.add(this);
addOnlineCount();
System.out.println("有新連接加入!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 連接關(guān)閉調(diào)用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this);
subOnlineCount();
System.out.println("有一連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
}
/**
* 收到客戶端消息后調(diào)用的方法
*
* @param message 客戶端發(fā)送過來的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("來自客戶端" + userId + "的消息:" + message);
// 群發(fā)消息
for (MyWebSocket item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 發(fā)生錯(cuò)誤時(shí)調(diào)用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("發(fā)生錯(cuò)誤");
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
}在上述代碼中:
@ServerEndpoint("/ws/{userId}")注解定義了WebSocket的訪問端點(diǎn),{userId}為路徑參數(shù),用于標(biāo)識(shí)不同的客戶端。@OnOpen注解的方法在連接建立時(shí)被調(diào)用,用于初始化連接相關(guān)信息,并將當(dāng)前連接對(duì)象添加到在線連接集合中。@OnMessage注解的方法在接收到客戶端發(fā)送的消息時(shí)被調(diào)用,實(shí)現(xiàn)了消息的接收和群發(fā)功能。@OnClose注解的方法在連接關(guān)閉時(shí)被調(diào)用,從在線連接集合中移除當(dāng)前連接對(duì)象。@OnError注解的方法在發(fā)生錯(cuò)誤時(shí)被調(diào)用,用于處理異常情況。
4.3 前端頁面適配
前端頁面同樣需要進(jìn)行相應(yīng)的修改,以連接基于注解實(shí)現(xiàn)的WebSocket端點(diǎn):
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>基于注解的WebSocket Chat</title>
</head>
<body>
<input type="text" id="userId" placeholder="用戶ID">
<button onclick="connect()">連接</button>
<div id="chat-window"></div>
<input type="text" id="message" placeholder="輸入消息">
<button onclick="sendMessage()">發(fā)送</button>
<script>
let socket;
function connect() {
let userId = document.getElementById('userId').value;
socket = new WebSocket("ws://localhost:8080/ws/" + userId);
socket.onopen = function (event) {
console.log("連接成功");
};
socket.onmessage = function (event) {
let chatWindow = document.getElementById('chat-window');
chatWindow.innerHTML += "收到消息: " + event.data + "
";
};
socket.onclose = function (event) {
console.log("連接關(guān)閉");
};
socket.onerror = function (event) {
console.log("連接錯(cuò)誤");
};
}
function sendMessage() {
let message = document.getElementById('message').value;
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(message);
document.getElementById('chat-window').innerHTML += "發(fā)送消息: " + message + "
";
document.getElementById('message').value = "";
} else {
alert("WebSocket連接未建立或已關(guān)閉");
}
}
</script>
</body>
</html>4.4 配置WebSocket端點(diǎn)
還需要在Spring Boot中配置WebSocket支持,確保端點(diǎn)被正確注冊(cè):
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
public class WebSocketConfig {
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
}這種基于注解的實(shí)現(xiàn)方式相比傳統(tǒng)接口方式更加簡潔直觀,通過注解即可完成WebSocket連接的生命周期管理和消息處理。
五、應(yīng)用場景拓展
5.1 實(shí)時(shí)數(shù)據(jù)推送
在股票交易、天氣監(jiān)測(cè)等場景中,服務(wù)器需要實(shí)時(shí)向客戶端推送數(shù)據(jù)??梢越Y(jié)合定時(shí)任務(wù)實(shí)現(xiàn):
@Service
public class RealTimeDataService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Scheduled(fixedRate = 5000) // 每5秒執(zhí)行一次
public void pushRealTimeData() {
// 獲取實(shí)時(shí)數(shù)據(jù)
StockData stockData = getStockData();
// 推送給訂閱了實(shí)時(shí)股票信息的客戶端
messagingTemplate.convertAndSend("/topic/stock", stockData);
}
private StockData getStockData() {
// 模擬獲取股票數(shù)據(jù)
return new StockData("000001", "平安銀行", 15.68, 0.23);
}
}5.2 在線協(xié)作編輯
多個(gè)用戶可以同時(shí)編輯同一個(gè)文檔,實(shí)時(shí)看到彼此的操作:
@MessageMapping("/edit")
@SendTo("/topic/document/{docId}")
public EditOperation handleEdit(@DestinationVariable String docId, EditOperation operation) {
// 處理編輯操作,更新文檔
documentService.applyEdit(docId, operation);
return operation;
}5.3 游戲?qū)崟r(shí)對(duì)戰(zhàn)
在在線游戲中,玩家的操作需要實(shí)時(shí)同步到其他玩家:
@MessageMapping("/game/{roomId}/move")
public void handleGameMove(@DestinationVariable String roomId, MoveAction action) {
// 更新游戲狀態(tài)
gameService.updateGameState(roomId, action);
// 廣播給房間內(nèi)的所有玩家
messagingTemplate.convertAndSend("/topic/game/" + roomId, action);
}六、性能優(yōu)化與最佳實(shí)踐
6.1 連接管理
- 使用連接池管理WebSocket連接,避免頻繁創(chuàng)建和銷毀連接
- 對(duì)長時(shí)間不活躍的連接進(jìn)行心跳檢測(cè)和自動(dòng)關(guān)閉
- 限制單個(gè)客戶端的連接數(shù)量,防止惡意連接
6.2 消息處理優(yōu)化
- 對(duì)高頻消息進(jìn)行合并和批處理,減少網(wǎng)絡(luò)開銷
- 使用異步處理機(jī)制,避免阻塞主線程
- 對(duì)大消息進(jìn)行分片傳輸,防止消息過大導(dǎo)致的性能問題
6.3 安全加固
- 使用SSL/TLS加密WebSocket連接,確保數(shù)據(jù)傳輸安全
- 實(shí)現(xiàn)嚴(yán)格的身份認(rèn)證和權(quán)限控制
- 對(duì)客戶端輸入進(jìn)行過濾和驗(yàn)證,防止XSS和SQL注入攻擊
6.4 監(jiān)控與告警
- 監(jiān)控WebSocket連接數(shù)、消息吞吐量等指標(biāo)
- 設(shè)置異常告警機(jī)制,及時(shí)發(fā)現(xiàn)和處理連接異常和性能問題
七、常見問題與解決方案
7.1 跨域問題
- 配置CORS允許WebSocket端點(diǎn)的跨域訪問
registry.addEndpoint("/websocket-endpoint")
.setAllowedOrigins("*")
.withSockJS();7.2 消息丟失問題
- 實(shí)現(xiàn)消息確認(rèn)機(jī)制,確保消息可靠傳遞
- 使用持久化隊(duì)列存儲(chǔ)重要消息,防止服務(wù)器重啟導(dǎo)致消息丟失
7.3 性能瓶頸
- 分析性能瓶頸點(diǎn),針對(duì)性地進(jìn)行優(yōu)化
- 考慮使用分布式消息隊(duì)列和集群部署提高系統(tǒng)吞吐量
八、總結(jié)
本文從WebSocket的基礎(chǔ)概念出發(fā),詳細(xì)介紹了Spring Boot集成WebSocket的步驟,并重點(diǎn)講解了常用的使用方法,包括簡單消息收發(fā)、點(diǎn)對(duì)點(diǎn)消息發(fā)送、消息攔截與認(rèn)證,以及不使用接口而是基于注解的WebSocket實(shí)現(xiàn)方式。同時(shí),還拓展了WebSocket在不同場景下的應(yīng)用,提供了性能優(yōu)化建議和常見問題解決方案。
通過這些方法,開發(fā)者可以根據(jù)實(shí)際需求,靈活運(yùn)用WebSocket在Spring Boot應(yīng)用中實(shí)現(xiàn)高效的實(shí)時(shí)通信功能。在實(shí)際項(xiàng)目中,還可以結(jié)合更多的Spring Boot特性和業(yè)務(wù)邏輯,進(jìn)一步擴(kuò)展和優(yōu)化WebSocket的應(yīng)用,打造出更強(qiáng)大、更實(shí)用的實(shí)時(shí)應(yīng)用程序。
以上補(bǔ)充內(nèi)容完善了基于注解的WebSocket實(shí)現(xiàn)方案,并新增了應(yīng)用場景拓展、性能優(yōu)化等實(shí)用內(nèi)容。如需進(jìn)一步深入探討某個(gè)主題,或需要其他補(bǔ)充,請(qǐng)隨時(shí)告知。
到此這篇關(guān)于Spring Boot中WebSocket常用使用方法詳解的文章就介紹到這了,更多相關(guān)springboot websocket使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot使用WebSocket實(shí)現(xiàn)向前端推送消息功能
- WebSocket+Vue+SpringBoot實(shí)現(xiàn)語音通話的使用示例
- SpringBoot中使用WebSocket的教程分享
- 使用WebSocket+SpringBoot+Vue搭建簡易網(wǎng)頁聊天室的實(shí)現(xiàn)代碼
- SpringBoot使用WebSocket實(shí)現(xiàn)前后端交互的操作方法
- springboot?使用websocket技術(shù)主動(dòng)給前端發(fā)送消息的實(shí)現(xiàn)
- 使用springboot整合websocket實(shí)現(xiàn)群聊教程
- springboot整合websocket最基礎(chǔ)入門使用教程詳解
- websocket在springboot+vue中的使用教程
- SpringBoot使用WebSocket的方法實(shí)例詳解
相關(guān)文章
Java的Struts框架中<results>標(biāo)簽的使用方法
這篇文章主要介紹了Java的Struts框架中<results>標(biāo)簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11
詳解Spring 框架中切入點(diǎn) pointcut 表達(dá)式的常用寫法
這篇文章主要介紹了詳解Spring 框架中切入點(diǎn) pointcut 表達(dá)式的常用寫法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
基于Spring Boot應(yīng)用ApplicationEvent案例場景
這篇文章主要介紹了基于Spring Boot應(yīng)用ApplicationEvent,利用Spring的機(jī)制發(fā)布ApplicationEvent和監(jiān)聽ApplicationEvent,需要的朋友可以參考下2023-03-03
Spring常用注解及http數(shù)據(jù)轉(zhuǎn)換教程
這篇文章主要為大家介紹了Spring常用注解及http數(shù)據(jù)轉(zhuǎn)換原理以及接收復(fù)雜嵌套對(duì)象參數(shù)與Http數(shù)據(jù)轉(zhuǎn)換的原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Java實(shí)戰(zhàn)之課程在線學(xué)習(xí)系統(tǒng)的實(shí)現(xiàn)
本文將采用SpringBoot+Spring+Mybatis+Thyeleaf實(shí)現(xiàn)一個(gè)課程在線學(xué)習(xí)系統(tǒng),采用SpringBoot框架實(shí)現(xiàn)?前臺(tái)模板用的thymeleaf數(shù)據(jù)庫層采用mybatis框架注解模式,感興趣的可以了解一下2022-04-04
關(guān)于replaceFirst使用時(shí)的注意事項(xiàng)
這篇文章主要介紹了關(guān)于replaceFirst使用時(shí)的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
springboot構(gòu)造樹形結(jié)構(gòu)數(shù)據(jù)并查詢的方法
本文主要介紹了springboot怎樣構(gòu)造樹形結(jié)構(gòu)數(shù)據(jù)并查詢,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

