Java socket編程實(shí)戰(zhàn)教程
一、什么是socket
Socket(套接字)是計(jì)算機(jī)網(wǎng)絡(luò)編程中用于實(shí)現(xiàn)不同設(shè)備之間通信的一種技術(shù)或接口。它提供了一套標(biāo)準(zhǔn)的編程接口,讓?xiě)?yīng)用程序能夠通過(guò)網(wǎng)絡(luò)發(fā)送和接收數(shù)據(jù)。
簡(jiǎn)單來(lái)說(shuō),Socket 就像是兩個(gè)設(shè)備(如計(jì)算機(jī)、服務(wù)器)之間通信的 "端口" 或 "接口":
一個(gè)設(shè)備上的應(yīng)用程序通過(guò) Socket 發(fā)送數(shù)據(jù)
另一個(gè)設(shè)備上的應(yīng)用程序通過(guò)對(duì)應(yīng)的 Socket 接收數(shù)據(jù)
Socket 通信通常基于 TCP/IP 協(xié)議,主要涉及兩個(gè)核心概念:
IP 地址:標(biāo)識(shí)網(wǎng)絡(luò)中的設(shè)備
端口號(hào):標(biāo)識(shí)設(shè)備上的特定應(yīng)用程序
在編程中,使用 Socket 通常需要以下步驟:
創(chuàng)建 Socket 對(duì)象
綁定到特定的 IP 地址和端口(服務(wù)器端)
建立連接(客戶端)或監(jiān)聽(tīng)連接(服務(wù)器端)
發(fā)送 / 接收數(shù)據(jù)
關(guān)閉連接
Socket 編程是網(wǎng)絡(luò)編程的基礎(chǔ),無(wú)論是網(wǎng)頁(yè)瀏覽、即時(shí)聊天、文件傳輸?shù)染W(wǎng)絡(luò)應(yīng)用,底層都依賴(lài) Socket 來(lái)實(shí)現(xiàn)數(shù)據(jù)通信。
例如,當(dāng)你訪問(wèn)一個(gè)網(wǎng)站時(shí),你的瀏覽器會(huì)創(chuàng)建一個(gè) Socket 連接到網(wǎng)站服務(wù)器的特定端口(通常是 80 或 443 端口),然后通過(guò)這個(gè)連接發(fā)送請(qǐng)求并接收網(wǎng)頁(yè)數(shù)據(jù)。
1、如何實(shí)現(xiàn)兩個(gè)設(shè)備上的通信
要實(shí)現(xiàn)兩個(gè)設(shè)備間通過(guò) Socket 進(jìn)行數(shù)據(jù)傳輸,通常需要?jiǎng)?chuàng)建一個(gè)服務(wù)器端和一個(gè)客戶端程序。以下是基于 Java 的實(shí)現(xiàn)示例,展示了基本的 Socket 通信流程:
實(shí)現(xiàn)原理
1)、服務(wù)器端創(chuàng)建 ServerSocket 并監(jiān)聽(tīng)指定端口
2)、客戶端創(chuàng)建 Socket 并連接到服務(wù)器的 IP 和端口
創(chuàng)建連接時(shí)的方法:
connect()方法
在 Java 的 Socket 編程中,connect() 是 Socket 類(lèi)的一個(gè)方法,用于客戶端主動(dòng)與服務(wù)器建立網(wǎng)絡(luò)連接。它的核心作用是發(fā)起 TCP 連接請(qǐng)求,與服務(wù)器的 ServerSocket 建立通信鏈路。
基本用法
connect() 方法有兩種常用重載形式:
// 形式1:指定服務(wù)器地址和端口,無(wú)超時(shí)設(shè)置
void connect(SocketAddress endpoint) throws IOException
// 形式2:指定服務(wù)器地址、端口和超時(shí)時(shí)間(毫秒)
void connect(SocketAddress endpoint, int timeout) throws IOException使用示例:
import java.net.Socket;
import java.net.InetSocketAddress;
import java.io.IOException;
public class ConnectExample {
public static void main(String[] args) {
String serverIP = "127.0.0.1"; // 服務(wù)器IP
int port = 8888; // 服務(wù)器端口
try (Socket socket = new Socket()) {
// 創(chuàng)建服務(wù)器地址對(duì)象
InetSocketAddress serverAddr = new InetSocketAddress(serverIP, port);
// 發(fā)起連接(設(shè)置超時(shí)時(shí)間為3秒)
socket.connect(serverAddr, 3000);
if (socket.isConnected()) {
System.out.println("連接成功!");
// 連接成功后可進(jìn)行數(shù)據(jù)傳輸...
}
} catch (IOException e) {
System.out.println("連接失敗:" + e.getMessage());
}
}
}內(nèi)部工作原理
connect() 方法的底層遵循 TCP 協(xié)議的 "三次握手" 過(guò)程:
客戶端向服務(wù)器發(fā)送 SYN 包(連接請(qǐng)求)
服務(wù)器收到后返回 SYN+ACK 包(同意連接)
客戶端再發(fā)送 ACK 包(確認(rèn)連接)
三次握手完成后,connect() 方法返回,連接建立
關(guān)鍵特點(diǎn)
阻塞特性:connect() 是阻塞方法,會(huì)一直等待直到連接建立或失敗
超時(shí)設(shè)置:帶超時(shí)參數(shù)的重載方法可以避免無(wú)限期等待(推薦使用)
異常情況:
服務(wù)器未啟動(dòng) → Connection refused
網(wǎng)絡(luò)不可達(dá) → No route to host
超時(shí)未響應(yīng) → Connection timed out
地址 / 端口無(wú)效 → IllegalArgumentException
與直接構(gòu)造 Socket 的區(qū)別
除了顯式調(diào)用 connect(),也可以通過(guò) Socket 構(gòu)造方法直接建立連接:
// 構(gòu)造方法直接連接(等價(jià)于先new Socket()再調(diào)用connect())
Socket socket = new Socket(serverIP, port);
兩種方式的區(qū)別:
顯式 connect() 更靈活,可先創(chuàng)建 Socket 對(duì)象,延遲連接或設(shè)置超時(shí)
構(gòu)造方法連接無(wú)法單獨(dú)設(shè)置超時(shí)(默認(rèn)超時(shí)較長(zhǎng))
connect() 是客戶端 Socket 建立連接的核心方法,它是 TCP 通信的起點(diǎn),只有調(diào)用成功后,客戶端才能與服務(wù)器進(jìn)行數(shù)據(jù)傳輸。
3)、連接建立后,通過(guò)輸入輸出流進(jìn)行數(shù)據(jù)傳輸
4)、通信完成后關(guān)閉連接
示例代碼:
Client.java (客戶端)
import java.io.*;
import java.net.*;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
// 服務(wù)器IP地址和端口,實(shí)際使用時(shí)替換為服務(wù)器的真實(shí)IP
String serverAddress = "127.0.0.1"; // 本地測(cè)試使用localhost
int port = 8888;
try (Socket socket = new Socket(serverAddress, port)) {
System.out.println("已連接到服務(wù)器: " + serverAddress + ":" + port);
// 使用try-with-resources自動(dòng)關(guān)閉資源
try (
// 獲取輸出流,向服務(wù)器發(fā)送數(shù)據(jù)
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true);
// 獲取輸入流,讀取服務(wù)器返回的數(shù)據(jù)
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
// 從控制臺(tái)讀取用戶輸入
Scanner scanner = new Scanner(System.in)
) {
String userInput;
// 循環(huán)讀取用戶輸入并發(fā)送給服務(wù)器
do {
System.out.print("請(qǐng)輸入要發(fā)送的消息(輸入bye退出): ");
userInput = scanner.nextLine();
// 發(fā)送消息到服務(wù)器
out.println(userInput);
// 讀取服務(wù)器的響應(yīng)
String response = in.readLine();
System.out.println("服務(wù)器響應(yīng): " + response);
} while (!"bye".equalsIgnoreCase(userInput));
}
} catch (UnknownHostException e) {
System.out.println("無(wú)法識(shí)別服務(wù)器地址: " + serverAddress);
} catch (IOException e) {
System.out.println("與服務(wù)器連接失敗: " + e.getMessage());
}
}
}Server.java (服務(wù)端)
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) {
// 定義服務(wù)器端口
int port = 8888;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("服務(wù)器啟動(dòng),監(jiān)聽(tīng)端口: " + port);
// 等待客戶端連接
while (true) {
// 阻塞等待客戶端連接
Socket clientSocket = serverSocket.accept();
System.out.println("客戶端已連接: " + clientSocket.getInetAddress());
// 使用try-with-resources自動(dòng)關(guān)閉資源
try (
// 獲取輸入流,讀取客戶端發(fā)送的數(shù)據(jù)
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
// 獲取輸出流,向客戶端發(fā)送數(shù)據(jù)
PrintWriter out = new PrintWriter(
clientSocket.getOutputStream(), true)
) {
String inputLine;
// 讀取客戶端發(fā)送的信息
while ((inputLine = in.readLine()) != null) {
System.out.println("收到客戶端消息: " + inputLine);
// 向客戶端發(fā)送響應(yīng)
out.println("服務(wù)器已收到: " + inputLine);
// 如果客戶端發(fā)送"bye",則關(guān)閉連接
if ("bye".equalsIgnoreCase(inputLine)) {
break;
}
}
} catch (IOException e) {
System.out.println("與客戶端通信時(shí)發(fā)生錯(cuò)誤: " + e.getMessage());
} finally {
try {
clientSocket.close();
System.out.println("客戶端連接已關(guān)閉");
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
System.out.println("服務(wù)器啟動(dòng)失敗: " + e.getMessage());
}
}
}運(yùn)行說(shuō)明
首先編譯并運(yùn)行 Server 類(lèi),服務(wù)器將啟動(dòng)并監(jiān)聽(tīng) 8888 端口
然后編譯并運(yùn)行 Client 類(lèi),客戶端將連接到服務(wù)器
在客戶端控制臺(tái)輸入消息,這些消息將發(fā)送到服務(wù)器
服務(wù)器收到消息后會(huì)返回響應(yīng)
輸入 "bye" 可以關(guān)閉連接
跨設(shè)備通信注意事項(xiàng)
確保兩個(gè)設(shè)備在同一網(wǎng)絡(luò)中,或能通過(guò)互聯(lián)網(wǎng)相互訪問(wèn)
客戶端需要使用服務(wù)器的實(shí)際 IP 地址(不能用 127.0.0.1)
檢查防火墻設(shè)置,確保通信端口(示例中為 8888)未被阻塞
實(shí)際應(yīng)用中通常需要處理多客戶端連接,可使用多線程實(shí)現(xiàn)這種基于 TCP 的 Socket 通信是可靠的,適用于需要保證數(shù)據(jù)完整性的場(chǎng)景,如文件傳輸、即時(shí)通訊等。
2、連接狀態(tài)管理(心跳 + 重連)的必要性
在網(wǎng)絡(luò)通信中,心跳(Heartbeat) 是一種用于檢測(cè)連接是否仍然有效的機(jī)制,而心跳包(Heartbeat Packet) 則是實(shí)現(xiàn)這種機(jī)制所發(fā)送的特殊數(shù)據(jù)包。
核心作用
當(dāng)兩個(gè)設(shè)備(如客戶端和服務(wù)器)通過(guò)網(wǎng)絡(luò)建立連接后,可能會(huì)出現(xiàn)以下情況:
- 物理連接已斷開(kāi)(如網(wǎng)線拔出、網(wǎng)絡(luò)故障),但雙方程序未感知
- 長(zhǎng)時(shí)間沒(méi)有數(shù)據(jù)傳輸,無(wú)法判斷連接是否存活
心跳機(jī)制通過(guò)定期發(fā)送小數(shù)據(jù)包來(lái)解決這些問(wèn)題,確保雙方知道連接的實(shí)時(shí)狀態(tài):
- 若接收方在規(guī)定時(shí)間內(nèi)收到心跳包,說(shuō)明連接正常
- 若超過(guò)指定時(shí)間未收到,則判定連接已斷開(kāi),觸發(fā)重連等處理
心跳包的特點(diǎn)
- 數(shù)據(jù)量小:通常是固定格式的空包或簡(jiǎn)單指令(如 "ping"),不占用太多網(wǎng)絡(luò)資源
- 周期性發(fā)送:按固定時(shí)間間隔發(fā)送(如每 30 秒、1 分鐘)
- 雙向或單向:
- 單向:客戶端定期向服務(wù)器發(fā)送心跳包
- 雙向:雙方互相發(fā)送,確保雙向可達(dá)性
實(shí)現(xiàn)示例(Java Socket)
以下是一個(gè)簡(jiǎn)單的心跳包實(shí)現(xiàn)邏輯:
1. 客戶端發(fā)送心跳包
// 客戶端心跳線程
class HeartbeatSender implements Runnable {
private Socket socket;
private boolean running = true;
public HeartbeatSender(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (OutputStream out = socket.getOutputStream()) {
while (running) {
// 發(fā)送心跳包(簡(jiǎn)單的字符串或空包)
out.write("HEARTBEAT".getBytes());
out.flush();
System.out.println("發(fā)送心跳包");
// 間隔30秒發(fā)送一次
Thread.sleep(30000);
}
} catch (Exception e) {
System.out.println("心跳發(fā)送失敗,連接可能已斷開(kāi)");
// 觸發(fā)重連邏輯
}
}
public void stop() {
running = false;
}
}2. 服務(wù)器接收心跳包并檢測(cè)超時(shí)
// 服務(wù)器心跳檢測(cè)線程
class HeartbeatChecker implements Runnable {
private Socket socket;
private boolean running = true;
private long lastHeartbeatTime;
private static final long TIMEOUT = 60000; // 超時(shí)時(shí)間60秒
public HeartbeatChecker(Socket socket) {
this.socket = socket;
this.lastHeartbeatTime = System.currentTimeMillis();
}
@Override
public void run() {
try (InputStream in = socket.getInputStream()) {
byte[] buffer = new byte[1024];
while (running) {
// 檢查是否超時(shí)
if (System.currentTimeMillis() - lastHeartbeatTime > TIMEOUT) {
System.out.println("心跳超時(shí),關(guān)閉連接");
socket.close();
running = false;
break;
}
// 讀取數(shù)據(jù)(包括心跳包)
if (in.available() > 0) {
int len = in.read(buffer);
String data = new String(buffer, 0, len);
if ("HEARTBEAT".equals(data)) {
// 收到心跳包,更新時(shí)間
lastHeartbeatTime = System.currentTimeMillis();
System.out.println("收到心跳包");
}
}
// 短暫休眠,減少CPU占用
Thread.sleep(1000);
}
} catch (Exception e) {
System.out.println("連接已斷開(kāi)");
}
}
}應(yīng)用場(chǎng)景
心跳機(jī)制廣泛用于需要長(zhǎng)期保持連接的場(chǎng)景:
- 即時(shí)通訊工具(如微信、QQ):維持在線狀態(tài)
- 物聯(lián)網(wǎng)設(shè)備:確保設(shè)備與服務(wù)器的實(shí)時(shí)連接
- 游戲服務(wù)器:檢測(cè)玩家是否掉線
- 分布式系統(tǒng):節(jié)點(diǎn)間的存活檢測(cè)
簡(jiǎn)單說(shuō),心跳包就像通信雙方定期互問(wèn) "你還在嗎",是保證網(wǎng)絡(luò)連接可靠性的重要機(jī)制。
在網(wǎng)絡(luò)通信中,重連(Reconnection) 是指當(dāng)客戶端與服務(wù)器的連接意外中斷后,自動(dòng)或手動(dòng)嘗試重新建立連接的機(jī)制。它是保證網(wǎng)絡(luò)通信可靠性的重要手段,尤其適用于需要長(zhǎng)期保持連接的場(chǎng)景(如即時(shí)通訊、物聯(lián)網(wǎng)設(shè)備、游戲客戶端等)。
重連的核心邏輯
重連機(jī)制通常包含以下關(guān)鍵步驟:
- 檢測(cè)連接斷開(kāi):通過(guò)心跳超時(shí)、IO 異常、連接狀態(tài)標(biāo)記等方式發(fā)現(xiàn)連接已中斷
- 嘗試重新連接:按照一定策略(如固定間隔、指數(shù)退避)重復(fù)調(diào)用
connect()方法 - 恢復(fù)通信狀態(tài):連接重建后,恢復(fù)之前的會(huì)話狀態(tài)(如重新登錄、同步數(shù)據(jù))
- 限制重連次數(shù):避免無(wú)限次重連導(dǎo)致資源浪費(fèi)或服務(wù)器壓力
Java Socket 重連實(shí)現(xiàn)示例
以下是一個(gè)客戶端自動(dòng)重連的實(shí)現(xiàn)代碼,包含了完整的檢測(cè)和重連邏輯:
import java.net.Socket;
import java.net.InetSocketAddress;
import java.io.IOException;
public class ReconnectClient {
private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 8888;
private static final int MAX_RECONNECT_ATTEMPTS = 10; // 最大重連次數(shù)
private static final int BASE_DELAY = 1000; // 基礎(chǔ)重連延遲(毫秒)
private Socket socket;
private boolean isRunning = true;
public static void main(String[] args) {
ReconnectClient client = new ReconnectClient();
client.start();
}
public void start() {
// 初始連接
if (connect()) {
// 連接成功后啟動(dòng)通信線程
startCommunication();
} else {
// 初始連接失敗,進(jìn)入重連流程
startReconnectLoop();
}
}
// 建立連接
private boolean connect() {
try {
socket = new Socket();
socket.connect(new InetSocketAddress(SERVER_IP, SERVER_PORT), 3000); // 3秒超時(shí)
System.out.println("成功連接到服務(wù)器:" + SERVER_IP + ":" + SERVER_PORT);
return true;
} catch (IOException e) {
System.out.println("連接失?。? + e.getMessage());
closeSocket();
return false;
}
}
// 啟動(dòng)通信線程(實(shí)際業(yè)務(wù)邏輯)
private void startCommunication() {
new Thread(() -> {
try {
// 這里是正常通信邏輯(讀取/發(fā)送數(shù)據(jù))
// ...
// 模擬通信過(guò)程中可能發(fā)生的異常斷開(kāi)
// Thread.sleep(5000);
// throw new IOException("模擬連接中斷");
} catch (Exception e) {
System.out.println("通信異常,連接斷開(kāi):" + e.getMessage());
closeSocket();
// 啟動(dòng)重連
startReconnectLoop();
}
}).start();
}
// 重連循環(huán)
private void startReconnectLoop() {
new Thread(() -> {
int attempt = 0;
while (isRunning && attempt < MAX_RECONNECT_ATTEMPTS) {
attempt++;
// 指數(shù)退避策略:重連間隔逐漸增加(1s, 2s, 4s...)
long delay = (long) (BASE_DELAY * Math.pow(2, Math.min(attempt, 10)));
System.out.println("第" + attempt + "次重連嘗試,延遲" + delay + "ms...");
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
// 嘗試重連
if (connect()) {
// 重連成功,恢復(fù)通信
startCommunication();
break;
}
}
if (attempt >= MAX_RECONNECT_ATTEMPTS) {
System.out.println("達(dá)到最大重連次數(shù),停止嘗試");
// 可以通知用戶或觸發(fā)其他處理
}
}).start();
}
// 關(guān)閉Socket
private void closeSocket() {
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
System.out.println("關(guān)閉Socket失?。? + e.getMessage());
}
socket = null;
}
// 停止客戶端
public void stop() {
isRunning = false;
closeSocket();
}
}重連策略詳解
- 重連觸發(fā)時(shí)機(jī):
- 心跳檢測(cè)超時(shí)(未收到服務(wù)器回應(yīng))
- 讀寫(xiě)數(shù)據(jù)時(shí)拋出
IOException(如Connection reset) - 調(diào)用
socket.isConnected()檢測(cè)到連接已關(guān)閉
- 重連間隔策略:
- 固定間隔:每次重連間隔相同時(shí)間(如每 3 秒嘗試一次)
- 指數(shù)退避:間隔時(shí)間逐漸增加(如 1s→2s→4s→8s,避免頻繁重試)
- 隨機(jī)延遲:在一定范圍內(nèi)隨機(jī)間隔,避免多個(gè)客戶端同時(shí)重連導(dǎo)致服務(wù)器壓力
- 重連限制:
- 最大嘗試次數(shù)(如 10 次)
- 最長(zhǎng)重連時(shí)間(如 5 分鐘)
- 達(dá)到限制后可提示用戶手動(dòng)干預(yù)
- 狀態(tài)恢復(fù):
- 重連成功后通常需要重新進(jìn)行身份驗(yàn)證(如發(fā)送登錄信息)
- 恢復(fù)中斷前的會(huì)話狀態(tài)(如重新請(qǐng)求未完成的數(shù)據(jù))
注意事項(xiàng)
- 重連過(guò)程中應(yīng)暫停正常業(yè)務(wù)邏輯,避免無(wú)效操作
- 確保重連線程與通信線程的資源同步(如使用鎖或原子變量)
- 對(duì)于需要保證消息可靠性的場(chǎng)景,應(yīng)在重連后處理未發(fā)送成功的消息
重連機(jī)制是構(gòu)建健壯網(wǎng)絡(luò)應(yīng)用的關(guān)鍵組件,合理的重連策略能顯著提升用戶體驗(yàn)和系統(tǒng)穩(wěn)定性。
到此這篇關(guān)于Java socket編程的文章就介紹到這了,更多相關(guān)Java socket編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java中的Socket編程使用方法詳解
- Java Socket 編程詳解
- Java使用線程池實(shí)現(xiàn)socket編程的方法詳解
- java實(shí)現(xiàn)基于TCP協(xié)議網(wǎng)絡(luò)socket編程(C/S通信)
- java實(shí)現(xiàn)基于UDP協(xié)議網(wǎng)絡(luò)Socket編程(C/S通信)
- Java編程實(shí)現(xiàn)基于TCP協(xié)議的Socket聊天室示例
- Java Socket編程服務(wù)器響應(yīng)客戶端實(shí)例代碼
- Java編程Socket實(shí)現(xiàn)多個(gè)客戶端連接同一個(gè)服務(wù)端代碼
相關(guān)文章
SpringMvc獲取請(qǐng)求頭請(qǐng)求體消息過(guò)程解析
這篇文章主要介紹了SpringMvc獲取請(qǐng)求頭請(qǐng)求體消息過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
如何使用Spring AOP優(yōu)雅實(shí)現(xiàn)橫切邏輯復(fù)用
本文詳細(xì)介紹了Spring AOP的核心概念、在Spring Boot中的使用方式、實(shí)現(xiàn)原理及多切面執(zhí)行順序,AOP作為面向?qū)ο缶幊痰难a(bǔ)充,通過(guò)橫切邏輯的分離,能有效簡(jiǎn)化日志、事務(wù)、權(quán)限等功能的開(kāi)發(fā),提升代碼的可維護(hù)性和復(fù)用性,感興趣的朋友跟隨小編一起看看吧2025-12-12
分布式面試消息隊(duì)列解決消息重復(fù)保證消息順序
這篇文章主要介紹了分布式面試問(wèn)題:分布式消息隊(duì)列如何解決消息重復(fù)并保證消息順序面試問(wèn)題解答,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
Java17中record替代Lombok部分功能使用場(chǎng)景探究
這篇文章主要介紹了使用Java17中的record替代Lombok的部分功能,本文來(lái)為大家小小的總結(jié)下,我們可以在哪些地方,利用record來(lái)替換Lombok2024-01-01
Spring Boot2集成AOPLog來(lái)記錄接口訪問(wèn)日志
這篇文章主要介紹了Spring Boot2集成AOPLog來(lái)記錄接口訪問(wèn)日志,日志是一個(gè)Web項(xiàng)目中必不可少的部分,借助它我們可以做許多事情,比如問(wèn)題排查、訪問(wèn)統(tǒng)計(jì)、監(jiān)控告警等,需要的朋友可以參考下2019-06-06

