Java Socket實(shí)現(xiàn)多人聊天系統(tǒng)
本文實(shí)例為大家分享了Java Socket實(shí)現(xiàn)多人聊天系統(tǒng)的具體代碼,供大家參考,具體內(nèi)容如下
前言
開(kāi)發(fā)環(huán)境:Eclipse Java 2019-06
注意:本項(xiàng)目只在單主機(jī)運(yùn)行調(diào)試過(guò),沒(méi)試過(guò)在局域網(wǎng)和不同主機(jī)之間接發(fā)消息和文件(估計(jì)不行),有需要的自行查閱資料。
一、多人聊天系統(tǒng)
1.1 客戶端
Login.java:登錄界面
// Login.java
package exp5;
import java.awt.*;
import javax.swing.*;
public class Login {
JTextField textField = null;
JPasswordField pwdField = null;
ClientReadAndPrint.LoginListen listener=null;
// 構(gòu)造函數(shù)
public Login() {
init();
}
void init() {
JFrame jf = new JFrame("登錄");
jf.setBounds(500, 250, 310, 210);
jf.setResizable(false); // 設(shè)置是否縮放
JPanel jp1 = new JPanel();
JLabel headJLabel = new JLabel("登錄界面");
headJLabel.setFont(new Font(null, 0, 35)); // 設(shè)置文本的字體類(lèi)型、樣式 和 大小
jp1.add(headJLabel);
JPanel jp2 = new JPanel();
JLabel nameJLabel = new JLabel("用戶名:");
textField = new JTextField(20);
JLabel pwdJLabel = new JLabel("密碼: ");
pwdField = new JPasswordField(20);
JButton loginButton = new JButton("登錄");
JButton registerButton = new JButton("注冊(cè)"); // 沒(méi)設(shè)置功能
jp2.add(nameJLabel);
jp2.add(textField);
jp2.add(pwdJLabel);
jp2.add(pwdField);
jp2.add(loginButton);
jp2.add(registerButton);
JPanel jp = new JPanel(new BorderLayout()); // BorderLayout布局
jp.add(jp1, BorderLayout.NORTH);
jp.add(jp2, BorderLayout.CENTER);
// 設(shè)置監(jiān)控
listener = new ClientReadAndPrint().new LoginListen(); // 新建監(jiān)聽(tīng)類(lèi)
listener.setJTextField(textField); // 調(diào)用PoliceListen類(lèi)的方法
listener.setJPasswordField(pwdField);
listener.setJFrame(jf);
pwdField.addActionListener(listener); // 密碼框添加監(jiān)聽(tīng)
loginButton.addActionListener(listener); // 按鈕添加監(jiān)聽(tīng)
jf.add(jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 設(shè)置關(guān)閉圖標(biāo)作用
jf.setVisible(true); // 設(shè)置可見(jiàn)
}
}
ChatView.java:登錄成功后的個(gè)人聊天界面
// ChatView.java
package exp5;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class ChatView {
String userName; //由客戶端登錄時(shí)設(shè)置
JTextField text;
JTextArea textArea;
ClientReadAndPrint.ChatViewListen listener;
// 構(gòu)造函數(shù)
public ChatView(String userName) {
this.userName = userName ;
init();
}
// 初始化函數(shù)
void init() {
JFrame jf = new JFrame("客戶端");
jf.setBounds(500,200,400,330); //設(shè)置坐標(biāo)和大小
jf.setResizable(false); // 縮放為不能縮放
JPanel jp = new JPanel();
JLabel lable = new JLabel("用戶:" + userName);
textArea = new JTextArea("***************登錄成功,歡迎來(lái)到多人聊天室!****************\n",12, 35);
textArea.setEditable(false); // 設(shè)置為不可修改
JScrollPane scroll = new JScrollPane(textArea); // 設(shè)置滾動(dòng)面板(裝入textArea)
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); // 顯示垂直條
jp.add(lable);
jp.add(scroll);
text = new JTextField(20);
JButton button = new JButton("發(fā)送");
JButton openFileBtn = new JButton("發(fā)送文件");
jp.add(text);
jp.add(button);
jp.add(openFileBtn);
// 設(shè)置“打開(kāi)文件”監(jiān)聽(tīng)
openFileBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showFileOpenDialog(jf);
}
});
// 設(shè)置“發(fā)送”監(jiān)聽(tīng)
listener = new ClientReadAndPrint().new ChatViewListen();
listener.setJTextField(text); // 調(diào)用PoliceListen類(lèi)的方法
listener.setJTextArea(textArea);
listener.setChatViewJf(jf);
text.addActionListener(listener); // 文本框添加監(jiān)聽(tīng)
button.addActionListener(listener); // 按鈕添加監(jiān)聽(tīng)
jf.add(jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 設(shè)置右上角關(guān)閉圖標(biāo)的作用
jf.setVisible(true); // 設(shè)置可見(jiàn)
}
// “打開(kāi)文件”調(diào)用函數(shù)
void showFileOpenDialog(JFrame parent) {
// 創(chuàng)建一個(gè)默認(rèn)的文件選擇器
JFileChooser fileChooser = new JFileChooser();
// 設(shè)置默認(rèn)顯示的文件夾
fileChooser.setCurrentDirectory(new File("C:/Users/Samven/Desktop"));
// 添加可用的文件過(guò)濾器(FileNameExtensionFilter 的第一個(gè)參數(shù)是描述, 后面是需要過(guò)濾的文件擴(kuò)展名)
// fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("(txt)", "txt"));
// 設(shè)置默認(rèn)使用的文件過(guò)濾器(FileNameExtensionFilter 的第一個(gè)參數(shù)是描述, 后面是需要過(guò)濾的文件擴(kuò)展名 可變參數(shù))
fileChooser.setFileFilter(new FileNameExtensionFilter("(txt)", "txt"));
// 打開(kāi)文件選擇框(線程將被堵塞,知道選擇框被關(guān)閉)
int result = fileChooser.showOpenDialog(parent); // 對(duì)話框?qū)?huì)盡量顯示在靠近 parent 的中心
// 點(diǎn)擊確定
if(result == JFileChooser.APPROVE_OPTION) {
// 獲取路徑
File file = fileChooser.getSelectedFile();
String path = file.getAbsolutePath();
ClientFileThread.outFileToServer(path);
}
}
}
Client.java:客戶端
// Client.java
package exp5;
import java.net.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
public class Client {
// 主函數(shù),新建登錄窗口
public static void main(String[] args) {
new Login();
}
}
/**
* 負(fù)責(zé)客戶端的讀和寫(xiě),以及登錄和發(fā)送的監(jiān)聽(tīng)
* 之所以把登錄和發(fā)送的監(jiān)聽(tīng)放在這里,是因?yàn)橐蚕硪恍?shù)據(jù),比如mySocket,textArea
*/
class ClientReadAndPrint extends Thread{
static Socket mySocket = null; // 一定要加上static,否則新建線程時(shí)會(huì)清空
static JTextField textInput;
static JTextArea textShow;
static JFrame chatViewJFrame;
static BufferedReader in = null;
static PrintWriter out = null;
static String userName;
// 用于接收從服務(wù)端發(fā)送來(lái)的消息
public void run() {
try {
in = new BufferedReader(new InputStreamReader(mySocket.getInputStream())); // 輸入流
while (true) {
String str = in.readLine(); // 獲取服務(wù)端發(fā)送的信息
textShow.append(str + '\n'); // 添加進(jìn)聊天客戶端的文本區(qū)域
textShow.setCaretPosition(textShow.getDocument().getLength()); // 設(shè)置滾動(dòng)條在最下面
}
} catch (Exception e) {}
}
/**********************登錄監(jiān)聽(tīng)(內(nèi)部類(lèi))**********************/
class LoginListen implements ActionListener{
JTextField textField;
JPasswordField pwdField;
JFrame loginJFrame; // 登錄窗口本身
ChatView chatView = null;
public void setJTextField(JTextField textField) {
this.textField = textField;
}
public void setJPasswordField(JPasswordField pwdField) {
this.pwdField = pwdField;
}
public void setJFrame(JFrame jFrame) {
this.loginJFrame = jFrame;
}
public void actionPerformed(ActionEvent event) {
userName = textField.getText();
String userPwd = String.valueOf(pwdField.getPassword()); // getPassword方法獲得char數(shù)組
if(userName.length() >= 1 && userPwd.equals("123")) { // 密碼為123并且用戶名長(zhǎng)度大于等于1
chatView = new ChatView(userName); // 新建聊天窗口,設(shè)置聊天窗口的用戶名(靜態(tài))
// 建立和服務(wù)器的聯(lián)系
try {
InetAddress addr = InetAddress.getByName(null); // 獲取主機(jī)地址
mySocket = new Socket(addr,8081); // 客戶端套接字
loginJFrame.setVisible(false); // 隱藏登錄窗口
out = new PrintWriter(mySocket.getOutputStream()); // 輸出流
out.println("用戶【" + userName + "】進(jìn)入聊天室!"); // 發(fā)送用戶名給服務(wù)器
out.flush(); // 清空緩沖區(qū)out中的數(shù)據(jù)
} catch (IOException e) {
e.printStackTrace();
}
// 新建普通讀寫(xiě)線程并啟動(dòng)
ClientReadAndPrint readAndPrint = new ClientReadAndPrint();
readAndPrint.start();
// 新建文件讀寫(xiě)線程并啟動(dòng)
ClientFileThread fileThread = new ClientFileThread(userName, chatViewJFrame, out);
fileThread.start();
}
else {
JOptionPane.showMessageDialog(loginJFrame, "賬號(hào)或密碼錯(cuò)誤,請(qǐng)重新輸入!", "提示", JOptionPane.WARNING_MESSAGE);
}
}
}
/**********************聊天界面監(jiān)聽(tīng)(內(nèi)部類(lèi))**********************/
class ChatViewListen implements ActionListener{
public void setJTextField(JTextField text) {
textInput = text; // 放在外部類(lèi),因?yàn)槠渌胤揭惨玫?
}
public void setJTextArea(JTextArea textArea) {
textShow = textArea; // 放在外部類(lèi),因?yàn)槠渌胤揭惨玫?
}
public void setChatViewJf(JFrame jFrame) {
chatViewJFrame = jFrame; // 放在外部類(lèi),因?yàn)槠渌胤揭惨玫?
// 設(shè)置關(guān)閉聊天界面的監(jiān)聽(tīng)
chatViewJFrame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
out.println("用戶【" + userName + "】離開(kāi)聊天室!");
out.flush();
System.exit(0);
}
});
}
// 監(jiān)聽(tīng)執(zhí)行函數(shù)
public void actionPerformed(ActionEvent event) {
try {
String str = textInput.getText();
// 文本框內(nèi)容為空
if("".equals(str)) {
textInput.grabFocus(); // 設(shè)置焦點(diǎn)(可行)
// 彈出消息對(duì)話框(警告消息)
JOptionPane.showMessageDialog(chatViewJFrame, "輸入為空,請(qǐng)重新輸入!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
out.println(userName + "說(shuō):" + str); // 輸出給服務(wù)端
out.flush(); // 清空緩沖區(qū)out中的數(shù)據(jù)
textInput.setText(""); // 清空文本框
textInput.grabFocus(); // 設(shè)置焦點(diǎn)(可行)
// textInput.requestFocus(true); // 設(shè)置焦點(diǎn)(可行)
} catch (Exception e) {}
}
}
}
ClientFileThread.java:文件傳輸功能(客戶端)
// ClientFileThread.java
package exp5;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class ClientFileThread extends Thread{
private Socket socket = null;
private JFrame chatViewJFrame = null;
static String userName = null;
static PrintWriter out = null; // 普通消息的發(fā)送(Server.java傳來(lái)的值)
static DataInputStream fileIn = null;
static DataOutputStream fileOut = null;
static DataInputStream fileReader = null;
static DataOutputStream fileWriter = null;
public ClientFileThread(String userName, JFrame chatViewJFrame, PrintWriter out) {
ClientFileThread.userName = userName;
this.chatViewJFrame = chatViewJFrame;
ClientFileThread.out = out;
}
// 客戶端接收文件
public void run() {
try {
InetAddress addr = InetAddress.getByName(null); // 獲取主機(jī)地址
socket = new Socket(addr, 8090); // 客戶端套接字
fileIn = new DataInputStream(socket.getInputStream()); // 輸入流
fileOut = new DataOutputStream(socket.getOutputStream()); // 輸出流
// 接收文件
while(true) {
String textName = fileIn.readUTF();
long totleLength = fileIn.readLong();
int result = JOptionPane.showConfirmDialog(chatViewJFrame, "是否接受?", "提示",
JOptionPane.YES_NO_OPTION);
int length = -1;
byte[] buff = new byte[1024];
long curLength = 0;
// 提示框選擇結(jié)果,0為確定,1位取消
if(result == 0){
// out.println("【" + userName + "選擇了接收文件!】");
// out.flush();
File userFile = new File("C:\\Users\\Samven\\Desktop\\接受文件\\" + userName);
if(!userFile.exists()) { // 新建當(dāng)前用戶的文件夾
userFile.mkdir();
}
File file = new File("C:\\Users\\Samven\\Desktop\\接受文件\\" + userName + "\\"+ textName);
fileWriter = new DataOutputStream(new FileOutputStream(file));
while((length = fileIn.read(buff)) > 0) { // 把文件寫(xiě)進(jìn)本地
fileWriter.write(buff, 0, length);
fileWriter.flush();
curLength += length;
// out.println("【接收進(jìn)度:" + curLength/totleLength*100 + "%】");
// out.flush();
if(curLength == totleLength) { // 強(qiáng)制結(jié)束
break;
}
}
out.println("【" + userName + "接收了文件!】");
out.flush();
// 提示文件存放地址
JOptionPane.showMessageDialog(chatViewJFrame, "文件存放地址:\n" +
"C:\\Users\\Samven\\Desktop\\接受文件\\" +
userName + "\\" + textName, "提示", JOptionPane.INFORMATION_MESSAGE);
}
else { // 不接受文件
while((length = fileIn.read(buff)) > 0) {
curLength += length;
if(curLength == totleLength) { // 強(qiáng)制結(jié)束
break;
}
}
}
fileWriter.close();
}
} catch (Exception e) {}
}
// 客戶端發(fā)送文件
static void outFileToServer(String path) {
try {
File file = new File(path);
fileReader = new DataInputStream(new FileInputStream(file));
fileOut.writeUTF(file.getName()); // 發(fā)送文件名字
fileOut.flush();
fileOut.writeLong(file.length()); // 發(fā)送文件長(zhǎng)度
fileOut.flush();
int length = -1;
byte[] buff = new byte[1024];
while ((length = fileReader.read(buff)) > 0) { // 發(fā)送內(nèi)容
fileOut.write(buff, 0, length);
fileOut.flush();
}
out.println("【" + userName + "已成功發(fā)送文件!】");
out.flush();
} catch (Exception e) {}
}
}
1.2 服務(wù)器端
MultiChat.java:多人聊天系統(tǒng)界面(服務(wù)器端)
// MultiChat.java
package exp5;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;
public class MultiChat {
JTextArea textArea;
// 用于向文本區(qū)域添加信息
void setTextArea(String str) {
textArea.append(str+'\n');
textArea.setCaretPosition(textArea.getDocument().getLength()); // 設(shè)置滾動(dòng)條在最下面
}
// 構(gòu)造函數(shù)
public MultiChat() {
init();
}
void init() {
JFrame jf = new JFrame("服務(wù)器端");
jf.setBounds(500,100,450,500); // 設(shè)置窗口坐標(biāo)和大小
jf.setResizable(false); // 設(shè)置為不可縮放
JPanel jp = new JPanel(); // 新建容器
JLabel lable = new JLabel("==歡迎來(lái)到多人聊天系統(tǒng)(服務(wù)器端)==");
textArea = new JTextArea(23, 38); // 新建文本區(qū)域并設(shè)置長(zhǎng)寬
textArea.setEditable(false); // 設(shè)置為不可修改
JScrollPane scroll = new JScrollPane(textArea); // 設(shè)置滾動(dòng)面板(裝入textArea)
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // 顯示垂直條
jp.add(lable);
jp.add(scroll);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
jf.add(jp);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 設(shè)置關(guān)閉圖標(biāo)作用
jf.setVisible(true); // 設(shè)置可見(jiàn)
}
}
Server.java:服務(wù)器端
// Server.java
package exp5;
import java.io.*;
import java.net.*;
import java.util.*;
public class Server{
static ServerSocket server = null;
static Socket socket = null;
static List<Socket> list = new ArrayList<Socket>(); // 存儲(chǔ)客戶端
public static void main(String[] args) {
MultiChat multiChat = new MultiChat(); // 新建聊天系統(tǒng)界面
try {
// 在服務(wù)器端對(duì)客戶端開(kāi)啟文件傳輸?shù)木€程
ServerFileThread serverFileThread = new ServerFileThread();
serverFileThread.start();
server = new ServerSocket(8081); // 服務(wù)器端套接字(只能建立一次)
// 等待連接并開(kāi)啟相應(yīng)線程
while (true) {
socket = server.accept(); // 等待連接
list.add(socket); // 添加當(dāng)前客戶端到列表
// 在服務(wù)器端對(duì)客戶端開(kāi)啟相應(yīng)的線程
ServerReadAndPrint readAndPrint = new ServerReadAndPrint(socket, multiChat);
readAndPrint.start();
}
} catch (IOException e1) {
e1.printStackTrace(); // 出現(xiàn)異常則打印出異常的位置
}
}
}
/**
* 服務(wù)器端讀寫(xiě)類(lèi)線程
* 用于服務(wù)器端讀取客戶端的信息,并把信息發(fā)送給所有客戶端
*/
class ServerReadAndPrint extends Thread{
Socket nowSocket = null;
MultiChat multiChat = null;
BufferedReader in =null;
PrintWriter out = null;
// 構(gòu)造函數(shù)
public ServerReadAndPrint(Socket s, MultiChat multiChat) {
this.multiChat = multiChat; // 獲取多人聊天系統(tǒng)界面
this.nowSocket = s; // 獲取當(dāng)前客戶端
}
public void run() {
try {
in = new BufferedReader(new InputStreamReader(nowSocket.getInputStream())); // 輸入流
// 獲取客戶端信息并把信息發(fā)送給所有客戶端
while (true) {
String str = in.readLine();
// 發(fā)送給所有客戶端
for(Socket socket: Server.list) {
out = new PrintWriter(socket.getOutputStream()); // 對(duì)每個(gè)客戶端新建相應(yīng)的socket套接字
if(socket == nowSocket) { // 發(fā)送給當(dāng)前客戶端
out.println("(你)" + str);
}
else { // 發(fā)送給其它客戶端
out.println(str);
}
out.flush(); // 清空out中的緩存
}
// 調(diào)用自定義函數(shù)輸出到圖形界面
multiChat.setTextArea(str);
}
} catch (Exception e) {
Server.list.remove(nowSocket); // 線程關(guān)閉,移除相應(yīng)套接字
}
}
}
ServerFileThread.java:文件傳輸功能(服務(wù)器端)
// ServerFileThread.java
package exp5;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
public class ServerFileThread extends Thread{
ServerSocket server = null;
Socket socket = null;
static List<Socket> list = new ArrayList<Socket>(); // 存儲(chǔ)客戶端
public void run() {
try {
server = new ServerSocket(8090);
while(true) {
socket = server.accept();
list.add(socket);
// 開(kāi)啟文件傳輸線程
FileReadAndWrite fileReadAndWrite = new FileReadAndWrite(socket);
fileReadAndWrite.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class FileReadAndWrite extends Thread {
private Socket nowSocket = null;
private DataInputStream input = null;
private DataOutputStream output = null;
public FileReadAndWrite(Socket socket) {
this.nowSocket = socket;
}
public void run() {
try {
input = new DataInputStream(nowSocket.getInputStream()); // 輸入流
while (true) {
// 獲取文件名字和文件長(zhǎng)度
String textName = input.readUTF();
long textLength = input.readLong();
// 發(fā)送文件名字和文件長(zhǎng)度給所有客戶端
for(Socket socket: ServerFileThread.list) {
output = new DataOutputStream(socket.getOutputStream()); // 輸出流
if(socket != nowSocket) { // 發(fā)送給其它客戶端
output.writeUTF(textName);
output.flush();
output.writeLong(textLength);
output.flush();
}
}
// 發(fā)送文件內(nèi)容
int length = -1;
long curLength = 0;
byte[] buff = new byte[1024];
while ((length = input.read(buff)) > 0) {
curLength += length;
for(Socket socket: ServerFileThread.list) {
output = new DataOutputStream(socket.getOutputStream()); // 輸出流
if(socket != nowSocket) { // 發(fā)送給其它客戶端
output.write(buff, 0, length);
output.flush();
}
}
if(curLength == textLength) { // 強(qiáng)制退出
break;
}
}
}
} catch (Exception e) {
ServerFileThread.list.remove(nowSocket); // 線程關(guān)閉,移除相應(yīng)套接字
}
}
}
二、運(yùn)行效果
2.1 初始化
服務(wù)器端(先運(yùn)行Server.java)

登錄界面(接著運(yùn)行Client.java,運(yùn)行一次生成一個(gè)登錄界面)

這里我還沒(méi)有實(shí)現(xiàn)注冊(cè)功能,登錄的用戶名隨意(不為空即可),密碼是123。
2.2 登錄成功


2.3 發(fā)送信息

2.4 發(fā)送文件
打開(kāi)文件我設(shè)置了默認(rèn)路徑是在桌面。接受文件需要先在桌面創(chuàng)建一個(gè)名為“接受文件”的文件夾,用于存放所有用戶接收的文件。

如果出現(xiàn)無(wú)法發(fā)送文件,應(yīng)該是ClientFileThread.java那里的路徑問(wèn)題,路徑包括了電腦用戶的名字,比如我的是“Samven”,可以試試修改為自己的真實(shí)路徑。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 基于Java實(shí)現(xiàn)互聯(lián)網(wǎng)實(shí)時(shí)聊天系統(tǒng)(附源碼)
- Java?NIO實(shí)現(xiàn)聊天系統(tǒng)
- 基于Java網(wǎng)絡(luò)編程和多線程的多對(duì)多聊天系統(tǒng)
- Java 網(wǎng)絡(luò)編程之 TCP 實(shí)現(xiàn)簡(jiǎn)單的聊天系統(tǒng)
- Java網(wǎng)絡(luò)編程UDP實(shí)現(xiàn)多線程在線聊天
- Java網(wǎng)絡(luò)編程實(shí)例——簡(jiǎn)單模擬在線聊天
- Java使用TCP實(shí)現(xiàn)在線聊天的示例代碼
- Java GUI編程實(shí)現(xiàn)在線聊天室
- Java中使用websocket實(shí)現(xiàn)在線聊天功能
- java實(shí)現(xiàn)在線聊天系統(tǒng)
相關(guān)文章
Java入門(mén)基礎(chǔ)之Java的基本語(yǔ)法與Java所支持的數(shù)據(jù)類(lèi)型
這篇文章主要介紹了Java入門(mén)基礎(chǔ)之Java的基本語(yǔ)法與Java所支持的數(shù)據(jù)類(lèi)型,熟悉語(yǔ)法和數(shù)據(jù)類(lèi)型通常是了解一種編程語(yǔ)言的開(kāi)始,需要的朋友可以參考下2016-02-02
jcl與jul?log4j1?log4j2?logback日志系統(tǒng)機(jī)制及集成原理
這篇文章主要介紹了jcl與jul?log4j1?log4j2?logback的集成原理,Apache?Commons-logging?通用日志框架與日志系統(tǒng)的機(jī)制,有需要的朋友可以借鑒參考下2022-03-03
SpringBoot 快速實(shí)現(xiàn) api 加密的方法
在項(xiàng)目中,為了保證數(shù)據(jù)的安全,我們常常會(huì)對(duì)傳遞的數(shù)據(jù)進(jìn)行加密,常用的加密算法包括對(duì)稱(chēng)加密(AES)和非對(duì)稱(chēng)加密(RSA),本文給大家介紹SpringBoot 快速實(shí)現(xiàn) api 加密,感興趣的朋友一起看看吧2023-10-10
kotlin java 混合代碼 maven 打包實(shí)現(xiàn)
這篇文章主要介紹了kotlin java 混合代碼 maven 打包實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03

