首先说一下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博客