首先說一下mybatis中四大組件的作用,下面開發的插件攔截器會使用
四大組件Executor、StatementHandler、ParameterHandler、ResultSetHandler
Executor:
Executor 是 MyBatis 中的執行器,負責 SQL 語句的執行工作。它通過調度 StatementHandler、ParameterHandler 和 ResultSetHandler 來完成 SQL 的增刪改查操作。Executor 管理事務和緩存,可以是 SimpleExecutor、ReuseExecutor、BatchExecutor 或在開啟二級緩存時的 CachingExecutor。
ParameterHandler:
ParameterHandler 用于處理 SQL 語句的參數設置。它將 Mapper 接口方法的參數封裝,并負責將這些參數值傳遞給 PreparedStatement,以便進行預編譯和執行。ParameterHandler 通過 TypeHandler 接口完成 Java 類型到 JDBC 類型的轉換。
ResultSetHandler:
ResultSetHandler 負責處理 SQL 執行后的返回結果集(ResultSet)。它將結果集轉換并映射到 Java 對象,完成從數據庫字段到 Java 實體屬性的映射工作。ResultSetHandler 能夠處理多種結果集的復雜映射關系。
StatementHandler:
StatementHandler 是 MyBatis 中的語句處理器,它是四大對象的核心,起到承上啟下的作用。StatementHandler 負責使用 JDBC 的 Statement(包括 PreparedStatement 和 CallableStatement)執行實際的數據庫操作。它通過調用 ParameterHandler 來設置 SQL 參數,并通過調用 ResultSetHandler 來處理查詢結果。
實現思路:
1 首先我們需要對數據的結果集進行攔截,也就是說需要攔截這個接口的方法

2 得到數據返回的結果集后,對結果集轉換成 List 進行遍歷脫敏
3 脫敏的時候我們還需要判斷一下哪些字段需要進行脫敏,這里我們定義注解來標識需要脫敏的字段(在這個注解里面可以定義一些脫敏策略,比如對手機號的脫敏規則、身份證的脫敏規則等等)
4 遍歷的時候通過反射,獲取所有屬性
5 我這里的脫敏有三個條件
? ? ? ? a、必須帶有 脫敏標識注解
? ? ? ? b、必須是 String 類型
? ? ? ? c、不得為空,這里直接使用工具類來判斷 StringUtils.isEmpty()
6 脫敏條件達成后,就獲取該注解上的 脫敏策略,根據脫敏策略 對屬性進行脫敏
7 將脫敏后的結果重新設置到屬性上
具體實現步驟
1?定義注解和不同的脫敏策略
import java.util.function.Function;//這里的Function是jdk8新特性,自己了解一哈子
//提示:傳入一個函數執行
public interface Desensitizer extends Function<String,String> {}
//這里定義脫敏策略
public enum TuoMinStrategy {/*手機號*/PHONE(s->s.replaceAll("^(\\\\d{3})\\\\d{4}(\\\\d{4})$","$1****$2")),/*用戶名 匹配中文全部替換*/USERNAME(s->s.replaceAll("[\\u4e00-\\u9fa5]","*"));private final Desensitizer desensitizer;TuoMinStrategy(Desensitizer d) {desensitizer = d;}public Desensitizer getDesensitizer() {return desensitizer;}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;//作用于屬性上
@Target(ElementType.FIELD)
//運行時生效
@Retention(RetentionPolicy.RUNTIME)
public @interface TuoMin {/*** 脫敏策略* @return*/TuoMinStrategy strategy();}
2?脫敏插件核心類
注意:注解中的屬性,method和args不是硬背的,有技巧,如這個,
ResultSetHandler類,點進去,要攔截哪個方法,方法名直接復制,方法里的參數,直接copy?引用
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.stream.Stream;@Component //需要放入到 ioc 容器中攔截器才會生效哦
//指定要攔截的目標方法的注解
//<h1>1、這個就是指定需要攔截的方法,這里我們指定攔截返回結果集的方法</h1>
@Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args= Statement.class))
public class TongMinPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//<h1>2、得到數據返回的結果集,對結果集轉換成 List<Object> 進行遍歷脫敏</h1>List<Object> records = (List<Object>) invocation.proceed();System.out.println("records = " + records);// 遍歷數據 調用 tuoMin 這個方法records.forEach(this::tuoMin);return records;}private void tuoMin(Object source) {Class<?> sourceClass = source.getClass();System.out.println("sourceClass = " + sourceClass);MetaObject metaObject = SystemMetaObject.forObject(source);//這個東東是 mybatis 提供的,通過這個玩意我們可以得到屬性的值System.out.println("metaObject = " + metaObject);// 使用 stream 流/*** 1、得到所有屬性* 2、篩選出帶有 TuoMin.class 注解的屬性* 3、調用doTuoMin方法將這些屬性脫敏*/Stream.of(sourceClass.getDeclaredFields()).filter(field -> field.isAnnotationPresent(TuoMin.class)).forEach(field->doTuoMin(metaObject,field));}private void doTuoMin(MetaObject metaObject, Field field) {System.out.println("metaObject = " + metaObject);System.out.println("field = " + field);String name = field.getName();System.out.println("name = " + name);Object value = metaObject.getValue(name);//根據屬性名得到屬性值System.out.println("value = " + value);// 脫敏條件:必須為String類型,值不等于空if (String.class == metaObject.getGetterType(name) && StringUtils.hasText(value.toString())) {//獲取脫敏策略TuoMin tuoMin = field.getAnnotation(TuoMin.class);System.out.println("tuoMin = " + tuoMin);TuoMinStrategy type = tuoMin.strategy();System.out.println("type = " + type);// 調用策略正則表達式脫敏,得到結果Object apply = type.getDesensitizer().apply((String) value);System.out.println("apply = " + apply);// 將脫敏后的結果重新設置到屬性上metaObject.setValue(name,apply);}}}
3?在需要脫敏的pojo的字段上加上注解,如
@TableField(value = "title")
@TuoMin(strategy = TuoMinStrategy.USERNAME) //加上注解并指定脫敏策略
private String title;
4?測試,舉例
脫敏前:
張三達美脫敏后:
張 * * 美
部分文字和代碼引用博文
【MyBatis】脫敏插件_前端脫敏插件是什么-CSDN博客