MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
)ParameterHandler (getParameterObject, setParameters
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
Mybatis官方插件说明
Mybatis提供了一个接口、四个对象来对执行语句进行拦截处理,下面以工作中常用的数据脱敏需求为例做简单说明。总的来说,分为三个步骤:实现Interceptor接口并说明定义要拦截的对象、处理拦截逻辑、添加插件。
数据脱敏就是将从数据库中查询到的敏感数据以*号或其他特殊字符进行替换,然后再返回给业务层进行使用,有效的保护了用户的隐私。
package com.zero.simple.plugins;
import com.zero.simple.annotation.Desensitize;
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 java.lang.reflect.Field;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
/**
* @author zero
* @description DesensitizePlugin
* @date 2022/4/1 10:26
*/
@Intercepts(
@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = Statement.class))
public class DesensitizePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1、获取查询结果
List<Object> records = (List<Object>) invocation.proceed();
records.forEach(this::desensitize);
return records;
}
private void desensitize(Object source) {
// 2、获取数据的类型
Class<?> sourceClass = source.getClass();
// 3、包装数据
MetaObject metaObject = SystemMetaObject.forObject(source);
Arrays.stream(sourceClass.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Desensitize.class))
.forEach(field -> doDesensitize(metaObject, field));
}
private void doDesensitize(MetaObject metaObject, Field field) {
// 4、获取属性名
String name = field.getName();
// 5、获取属性值
Object value = metaObject.getValue(name);
if (String.class == metaObject.getGetterType(name) && value != null) {
Desensitize desensitize = field.getAnnotation(Desensitize.class);
DesensitizeStrategy strategy = desensitize.strategy();
// 6、处理属性值
String newString = strategy.getDesensitizer().apply((String) value);
// 7、重新给属性复制
metaObject.setValue(name, newString);
}
}
}
package com.zero.simple.plugins;
/**
* @author zero
* @description DesensitizeStrategy
* @date 2022/4/1 10:34
*/
public enum DesensitizeStrategy {
// 名称脱敏
NAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
;
private final Desensitizer desensitizer;
DesensitizeStrategy(Desensitizer desensitizer) {
this.desensitizer = desensitizer;
}
public Desensitizer getDesensitizer() {
return desensitizer;
}
}
package com.zero.simple.annotation;
import com.zero.simple.plugins.DesensitizeStrategy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author zero
* @description Desensitize
* @date 2022/4/1 10:30
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Desensitize {
DesensitizeStrategy strategy();
}
package com.zero.simple.plugins;
import java.util.function.Function;
/**
* @author zero
* @description Desensitizer
* @date 2022/4/1 10:33
*/
public interface Desensitizer extends Function<String, String> {
}
package com.zero.simple.model;
import com.zero.simple.annotation.Desensitize;
import com.zero.simple.plugins.DesensitizeStrategy;
import lombok.Data;
/**
* @author zero
* @description Country
* @date 2022/3/21 9:15
*/
@Data
public class Country {
private Long id;
@Desensitize(strategy = DesensitizeStrategy.NAME)
private String countryName;
private String countryCode;
}
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="application.properties"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
<typeAliases>
<package name="com.zero.simple.model"/>
typeAliases>
<plugins>
<plugin interceptor="com.zero.simple.plugins.DesensitizePlugin"/>
plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/CountryMapper.xml"/>
mappers>
configuration>