目錄
1.制作驗證碼——java SPI機制
1.1 類所屬包情況
1.2 具體實現
1.2.1 核心接口:ICode?
1.2.2 接口實現類:驗證碼的具體生成邏輯
1.2.3 服務工廠類:CodeServiceFactory(核心:SPI 服務發現)
1.2.4 SPI 配置文件
1.2.5 主程序:App(運行入口)
1.2.6 注釋掉不同的配置
2.Java RMI (遠程方法調用)
2.1 定義
2.2 實現分布式登錄驗證系統
2.2.1 數據庫準備
2.2.2 在idea中啟動Java的RMI服務
2.2.3 在eclipse實現登錄
1.制作驗證碼——java SPI機制
1.1 類所屬包情況
1.2 具體實現
1.2.1 核心接口:ICode?
定義驗證碼生成的標準接口,所有驗證碼生成實現類都需要實現該接口
package com.hy.interfaces;public interface ICode {public String makeCode();
}
1.2.2 接口實現類:驗證碼的具體生成邏輯
① 數字驗證碼實現:NumberCodeImpl
package com.hy.interfaces.impl;import java.util.Random;
import com.hy.interfaces.ICode;public class NumberCodeImpl implements ICode {// 數字字符源(0-9)private String[] nums = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };@Overridepublic String makeCode() {String code = "";for (int i = 0; i < 4; i++) { // 生成4位驗證碼// 隨機獲取字符源中的一個元素String s = String.valueOf(new Random().nextInt(nums.length));// 確保字符不重復:如果已包含則重新生成(i--回退循環)if (!code.contains(s)) {code += s;} else {i--;}}return code;}
}
② 中文驗證碼實現:ChineseCodeImpl
package com.hy.interfaces.impl;import java.util.Random;
import com.hy.interfaces.ICode;public class ChineseCodeImpl implements ICode {// 中文字符源(自定義漢字)private String[] nums = { "趙", "錢", "孫", "李", "王", "五", "馬", "六", "天", "地" };@Overridepublic String makeCode() {String code = "";for (int i = 0; i < 4; i++) { // 生成4位驗證碼// 隨機獲取字符源中的一個元素String s = nums[new Random().nextInt(nums.length)];// 確保字符不重復:如果已包含則重新生成(i--回退循環)if (!code.contains(s)) {code += s;} else {i--;}}return code;}
}
1.2.3 服務工廠類:CodeServiceFactory(核心:SPI 服務發現)
利用 Java 的 ServiceLoader 類實現 SPI 機制的服務發現:通過接口(ICode.class)動態加載其所有實現類
package com.hy.service;import java.util.Iterator;
import java.util.ServiceLoader;
import com.hy.interfaces.ICode;public class CodeServiceFactory {public static String createCode(Class targetClass) {// 1. 通過ServiceLoader動態加載實現了targetClass(此處為ICode)的服務實現類ServiceLoader s = ServiceLoader.load(targetClass);// 2. 獲取實現類的迭代器Iterator its = s.iterator();ICode code = null;// 3. 迭代獲取最后一個實現類(若有多個實現,取最后一個)while (its.hasNext()) {code = (ICode) its.next();}// 4. 調用實現類的makeCode()生成驗證碼String checkCode = code.makeCode();return checkCode;}
}
1.2.4 SPI 配置文件
#
?開頭的行是注釋,不會被加載,需要實現哪個,就把其余注釋掉
com.hy.interfaces.impl.NumberCodeImpl
#com.hy.interfaces.impl.ChineseCodeImpl
1.2.5 主程序:App(運行入口)
主方法通過無限循環,每 6 秒調用一次工廠類的createCode方法,生成驗證碼
package com.hy.javaspi;import com.hy.interfaces.ICode;
import com.hy.service.CodeServiceFactory;public class App {public static void main(String[] args) {while (true) { // 無限循環// 調用工廠類生成驗證碼(基于ICode接口的實現類)String checkCode = CodeServiceFactory.createCode(ICode.class);System.out.println("獲取的驗證碼為:" + checkCode);try {Thread.sleep(6000); // 每6秒生成一次} catch (InterruptedException e) {e.printStackTrace();}}}
}
1.2.6 注釋掉不同的配置
① 使用數字驗證碼
com.hy.interfaces.impl.NumberCodeImpl
#com.hy.interfaces.impl.ChineseCodeImpl
輸出結果:
獲取的驗證碼為:2834
獲取的驗證碼為:9651
獲取的驗證碼為:0753
獲取的驗證碼為:4702...
② 使用中文驗證碼
#com.hy.interfaces.impl.NumberCodeImpl
com.hy.interfaces.impl.ChineseCodeImpl
輸出結果:
獲取的驗證碼為:五李孫地
獲取的驗證碼為:錢孫六李
獲取的驗證碼為:趙馬地王
獲取的驗證碼為:馬五李地...
2.Java RMI (遠程方法調用)
2.1 定義
Java RMI(Remote Method Invocation,遠程方法調用)是 Java 原生的分布式通信機制,允許一個 JVM 中的對象(客戶端)調用另一個 JVM 中的對象(服務端)的方法,就像調用本地方法一樣,無需顯式處理網絡通信細節。
2.2 實現分布式登錄驗證系統
2.2.1 數據庫準備
-- 創建t_emps表 --
CREATE TABLE t_emps(eid INT PRIMARY KEY auto_increment, -- 員工的編號ename VARCHAR(20) NOT NULL, -- 員工的姓名epwd CHAR(8) NOT NULL, -- 員工的密碼ebirthday datetime, -- 員工的出生年月,不設計年齡字段,會造成字段冗余esalary DOUBLE NOT NULL, -- 員工的工資eaddress VARCHAR(100), -- 員工的地址estate INT NOT NULL -- 員工的狀態
)-- 刪除t_emps表 --
DROP TABLE t_emps-- 插入數據 --
INSERT INTO t_emps(ename,epwd,ebirthday,esalary,eaddress,estate)
VALUES('張三','11111','2000-05-28',90000.56,'南京',1);INSERT INTO t_emps(ename,epwd,ebirthday,esalary,eaddress,estate)
VALUES('李四','22222','2004-06-15',88000.69,'鹽城',1);INSERT INTO t_emps(ename,epwd,ebirthday,esalary,eaddress,estate)
VALUES('李老八','22222','1996-12-30',5600,'無錫',1);INSERT INTO t_emps(ename,epwd,ebirthday,esalary,eaddress,estate)
VALUES('趙二','22222','1996-12-30',5800,'無錫',0);-- 查詢表 --
SELECT * FROM t_emps
實現效果:
2.2.2 在idea中啟動Java的RMI服務
步驟一:在idea中新建Maven項目,并在pom.xml文件中添加依賴
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.24</version>
</dependency>
步驟二:具體實現
① 類所屬包情況
② 數據訪問層:Dao類(數據庫交互)
通過 JDBC 連接 MySQL 數據庫,實現 “登錄驗證” 的數據庫交互邏輯
package com.hy.dao;import java.sql.*;public class Dao {Connection conn; // 數據庫連接對象// 構造方法:初始化數據庫連接public Dao() {try {// 1. 加載MySQL JDBC驅動(MySQL 8.0+使用com.mysql.cj.jdbc.Driver)Class.forName("com.mysql.cj.jdbc.Driver");// 2. 建立數據庫連接// 連接URL:jdbc:mysql://主機:端口/數據庫名// 用戶名:root,密碼:修改為自己的密碼conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql2025", "root", "yourpassword");} catch (ClassNotFoundException e) {e.printStackTrace(); // 驅動類未找到異常} catch (SQLException e) {e.printStackTrace(); // 數據庫連接異常}}// 登錄驗證方法:檢查用戶名和密碼是否匹配public int checkLogin(String username, String userpwd) {// SQL查詢:統計符合條件的用戶數量(ename=用戶名且epwd=密碼)String sql = "select count(ename) from t_emps where ename = ? and epwd =?";int count = 0; // 匹配的用戶數量(0或1)try {// 使用PreparedStatement預編譯SQL,防止SQL注入PreparedStatement pstmt = this.conn.prepareStatement(sql);pstmt.setString(1, username); // 填充第一個參數(用戶名)pstmt.setString(2, userpwd); // 填充第二個參數(密碼)// 執行查詢,獲取結果集ResultSet rs = pstmt.executeQuery();// 讀取結果集中的計數(count(ename))while (rs.next()) {count = rs.getInt(1); // 第一列的結果(0或1)}} catch (SQLException e) {e.printStackTrace(); // SQL執行異常} finally {// 關閉數據庫連接(避免資源泄露)if (null != conn) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}return count; // 返回匹配數量(1表示登錄成功,0表示失敗)}
}
③ 遠程接口:IData(定義 RMI 通信標準)
定義客戶端與服務端的 “通信協議”,明確可遠程調用的方法,是 RMI 通信的基礎(客戶端與服務端必須完全一致)。
package com.hy.data.interfaces;import java.rmi.Remote;
import java.rmi.RemoteException;// 客戶端與服務端的遠程通信接口(必須繼承Remote)
public interface IData extends Remote {// 遠程方法:查詢消息(必須聲明拋出RemoteException)public String queryMessage() throws RemoteException;// 遠程方法:登錄驗證(必須聲明拋出RemoteException)public String checkLogin(String username, String userpwd) throws RemoteException;
}
④?遠程接口實現:UserDataImpl(服務端業務邏輯)
實現IData遠程接口,封裝服務端業務邏輯(調用 Dao 層操作數據庫),并通過 RMI 框架導出為 “可遠程訪問的對象”。
package com.hy.impl;import com.hy.dao.Dao;
import com.hy.data.interfaces.IData;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;// 遠程接口的實現類(必須繼承UnicastRemoteObject或手動實現序列化)
public class UserDataImpl extends UnicastRemoteObject implements IData {// 構造方法:必須拋出RemoteException(因為父類UnicastRemoteObject的構造方法拋出該異常)public UserDataImpl() throws RemoteException {super(); // 調用父類構造方法,自動處理對象的網絡傳輸(序列化/反序列化)}// 實現遠程方法:返回固定消息@Overridepublic String queryMessage() throws RemoteException {return "RMI分布式從遠程傳過來的數據為:RMI、webservice、hessian、thrift、googleRPC、Dubbo";}// 實現遠程方法:調用Dao進行登錄驗證@Overridepublic String checkLogin(String username, String userpwd) throws RemoteException {Dao dao = new Dao(); // 創建數據訪問對象// 調用Dao的checkLogin方法,若返回值>0(即存在匹配用戶),返回"登錄成功",否則返回"登錄失敗"if (dao.checkLogin(username, userpwd) > 0) {return "登錄成功";}return "登錄失敗";}
}
⑤?RMI 服務端:ServerRMI(啟動并發布服務)
啟動 RMI 注冊表(服務注冊中心)、創建遠程對象實例、將遠程對象綁定到指定 URL,供客戶端查找和調用。
package com.hy.serverrmi;import com.hy.data.interfaces.IData;
import com.hy.impl.UserDataImpl;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;public class ServerRMI {public static void main(String[] args) {try {// 1. 創建遠程對象實例(UserDataImpl實現了IData接口)IData datas = new UserDataImpl();// 2. 在本地9200端口注冊RMI注冊表(類似“服務注冊中心”)LocateRegistry.createRegistry(9200);// 3. 將遠程對象綁定到RMI URL(客戶端通過該URL查找服務)// 格式:rmi://主機:端口/服務名稱Naming.bind("rmi://127.0.0.1:9200/userdatas", datas);System.out.println("Java的RMI服務已經啟動成功");} catch (RemoteException e) {e.printStackTrace(); // 遠程對象創建或注冊表啟動異常} catch (MalformedURLException e) {e.printStackTrace(); // RMI URL格式錯誤} catch (AlreadyBoundException e) {e.printStackTrace(); // 服務名稱已被綁定(重復發布)}}
}
輸出結果:
Java的RMI服務已經啟動成功
2.2.3 在eclipse實現登錄
① 類所屬包情況
② 遠程接口:IData(客戶端與服務端的通信契約)
客戶端與服務端的 “通信協議”,定義客戶端可以遠程調用的方法。
package com.hy.data.interfaces;import java.rmi.Remote;
import java.rmi.RemoteException;// 遠程接口:客戶端和服務端必須共享此接口(包路徑、方法定義完全一致)
public interface IData extends Remote {// 遠程方法1:查詢消息(服務端返回預設字符串)public String queryMessage() throws RemoteException;// 遠程方法2:登錄驗證(接收用戶名和密碼,返回登錄結果)public String checkLogin(String username, String userpwd) throws RemoteException;
}
③?客戶端實現類:App(發起遠程調用的核心邏輯)
接收用戶輸入(賬號密碼),通過 RMI 框架查找服務端遠程對象,發起遠程調用,最后展示調用結果。
package com.hy.javamiclinet;import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.Scanner;
import com.hy.data.interfaces.IData;public class App {// 遠程對象引用:客戶端通過該引用調用服務端方法static IData data = null;// 靜態代碼塊:初始化遠程對象引用(程序啟動時執行)static {try {// 關鍵:通過RMI URL查找服務端綁定的遠程對象// URL格式:rmi://服務端IP:端口/服務名稱(需與服務端綁定的URL完全一致)data = (IData) Naming.lookup("rmi://127.0.0.1:9200/userdatas");} catch (MalformedURLException e) {e.printStackTrace(); // URL格式錯誤(如端口無效、協議錯誤)} catch (RemoteException e) {e.printStackTrace(); // 遠程通信異常(如服務端未啟動、網絡不通)} catch (NotBoundException e) {e.printStackTrace(); // 服務名稱未綁定(服務端未發布該服務)}}// 調用遠程方法:queryMessage(查詢消息)public void queryMsg() {try {// 看似調用本地對象方法,實際通過網絡調用服務端的實現String message = data.queryMessage();System.out.println("客戶端遠程調用服務端的結果為:" + message);} catch (RemoteException e) {e.printStackTrace(); // 遠程調用過程中發生異常}}// 調用遠程方法:checkLogin(登錄驗證)public void checkLogin(String username, String userpwd) {try {// 傳遞參數(用戶名和密碼)到服務端,調用遠程驗證方法String result = data.checkLogin(username, userpwd);System.out.println("客戶端遠程調用服務端登錄的結果為:" + result);} catch (RemoteException e) {e.printStackTrace(); // 遠程調用異常(如參數傳輸失敗、服務端處理出錯)}}// 主方法:程序入口,接收用戶輸入并發起登錄驗證public static void main(String[] args) {App app = new App(); // 創建客戶端實例// 接收用戶輸入(用戶名和密碼)System.out.println("請輸入用戶姓名:");Scanner s1 = new Scanner(System.in);String username = s1.next(); // 讀取用戶名System.out.println("請輸入用戶密碼:");Scanner s2 = new Scanner(System.in);String userpwd = s2.next(); // 讀取密碼// 調用登錄驗證方法(遠程調用)app.checkLogin(username, userpwd);// 可選:調用查詢消息方法(注釋掉了,取消注釋可執行)// app.queryMsg();}
}
輸出結果:
請輸入用戶姓名:
張三
請輸入用戶密碼:
11111
客戶端遠程調用服務端登錄的結果為:登錄成功
請輸入用戶姓名:
張三
請輸入用戶密碼:
22222
客戶端遠程調用服務端登錄的結果為:登錄失敗