Mybatis自定义插件实现

一、插件说明

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接口并说明定义要拦截的对象、处理拦截逻辑、添加插件。

数据脱敏就是将从数据库中查询到的敏感数据以*号或其他特殊字符进行替换,然后再返回给业务层进行使用,有效的保护了用户的隐私。

1、实现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);
        }
    }
}

2、业务处理

2.1定义处理策略
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;
    }
}
2.2定义脱敏注解
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();

}
2.3定义处理接口
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> {
}
2.4添加注解
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;
}

3、配置文件添加插件


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>

4、测试

Mybatis自定义插件实现_第1张图片

你可能感兴趣的:(源码分析,mysql,数据库,database,mybatis)