IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> mybatis插件自定义及原理 -> 正文阅读

[Java知识库]mybatis插件自定义及原理

一:概述
mybatis插件可以帮我们对mybatis的四大对象进行拦截处理,要使用好插件,我们需要先了解四大对象的作用,这样才能根据需求定义插件。

二:mybatis的四大对象

1.Executor:
负责调用StatementHandler操作数据库,以及二级缓存的处理(使用CachingExecutor进行包装)定义插件可以修改原生sql

2.StatementHandler:
负责sql脚本的执行以及mybatis一级缓存的处理,定义插件可以修改原生的sql,例如分页插件,数据权限等

3.ParameterHandler:
负责对参数的处理,定义插件可以修改对参数的处理

4.ResultSetHandler:
负责对结果集的处理,定义插件拦截可以修改对结果集处理的逻辑

三:自定义插件

1. 实现Interceptor接口

2.利用@Intercepts注解和@Signature注解定义需要拦截的方法

3.intercept方法中编写需要拦截后需要的代码逻辑

@Intercepts(value = {@Signature(type = Executor.class,
        method = "query",
        args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})})
public class MyInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Executor target = (Executor) invocation.getTarget();
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement)args[0];

        BoundSql boundSql = ms.getBoundSql(args[1]);
        String sql = boundSql.getSql();
        //TODO 2021/12/04 可以对原sql进行修改
        System.out.println("原sql:" + sql);
        return invocation.proceed();
    }
}

4.将自定义的插件加入到插件链中

@Configuration
public class InterceptorConfig {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @PostConstruct
    public void myInterceptor() {
        sqlSessionFactory.getConfiguration().addInterceptor(new MyInterceptor());
    }
}

四:插件的原理

以Executor为例:

在生成Executor的时候会对比遍历所有的插件对Executor进行代理(多层代理)。代码在Configuration的newExecutor中:

executor = (Executor) interceptorChain.pluginAll(executor);
for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
}

循环所有的插件对Executor对象进行包装,默认会调用Plugin的wrap()方法生成代理类

public static Object wrap(Object target, Interceptor interceptor) {
    //解析Intercepts注解 保存
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    //取得要改变行为的类(ParameterHandler|ResultSetHandler|StatementHandler|Executor)
    Class<?> type = target.getClass();
    //取得接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    //产生代理 所以之后会执行Plugin的invoke方法
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    //取Intercepts注解
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    //必须得有Intercepts注解,没有报错
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    //value是数组型,Signature的数组
    Signature[] sigs = interceptsAnnotation.value();
    //每个class里有多个Method需要被拦截,所以这么定义
    // 解析Signature注解中的信息
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        //获取Signature注解中配置的method
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

经过多层代理之后,返回之后就会依次执行Plugin的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      //看哪些方法需要拦截
      if (methods != null && methods.contains(method)) {
        //调用Interceptor.intercept,也即插入了我们自己的逻辑
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //不是需要拦截的方法,执行原来逻辑
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-05 11:54:42  更:2021-12-05 11:55:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:58:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码