带你学习Mybatis之拦截器

mybatis拦截器

mybatis拦截器也叫做插件,mybatis允许开发者自定义拦截器对SQL语句执行过程中的某一个点进行拦截。默认mybatis允许拦截Mybatis中的四大核心对象:Executor中的方法、ParameterHandler的方法、ResultSetHandler的方法以及StatementHandler中的方法

  • Executor中的update()、query()、flushStatements()、commit()、rollback()、getTransaction()、close()、isClose()方法,拦截执行器的方法
  • ParameterHandler中的getParameterObject()、setParameters()方法,拦截参数的处理
  • ResultSetHandler中的handlerResultSets()、handlerOutputParameters()方法,拦截结果集的处理
  • StatementHandler中的prepare()、parameterize()、batch()、update()、query()方法,拦截sql语句的构建

通过动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,当执行所拦截的方法时,就会进入拦截方法

Interceptor接口

mybatis使用拦截器需要实现Interceptor接口

复制代码
public interface Interceptor {
   // 覆盖所拦截对象的原有方法,执行拦截逻辑的方法
    Object intercept(Invocation var1) throws Throwable;
  // target为被拦截对象,作用是给被拦截对象生成一个代理对象
    default Object plugin(Object target) {
      // Plugin实现了InvocationHandler接口,wrap方法就是执行Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap))
       //使用JDK的动态代理,给target对象创建一个delegate代理对象,以此来实现方法拦截和增强功能,它会回调intercept()方法,使用当前拦截器来包装目标对象
      // 调用一次wrap方法产生一个代理对象,如果有第二个插件,会将第一个带对象传递给wrap方法,生成第二个代理对象,再调用Invocation.proceed会从最后一个代理对象的invoke方法运行到第一个代理对象的invoke方法,直至真实方法
        return Plugin.wrap(target, this);
    }
  // 根据配置初始化Interceptor对象,只在插件初始化的时候调用一次
    default void setProperties(Properties properties) {
    }
}

@Intercepts注解

自定义的拦截器除了实现Interceptor接口,还需要使用@Intercepts和@Signature注解进行标识

@Intercepts注解中指定了一个@Signature注解列表

复制代码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Intercepts {
    Signature[] value();
}

每个@Signature注解中标识了该插件需要拦截的方法信息

复制代码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
   //拦截的对象类型
    Class<?> type();
  //拦截的方法
    String method();
  //被拦截方法的参数列表
    Class<?>[] args();
}
使用示例
复制代码
@Intercepts(
        @Signature(type = StatementHandler.class,method = "prepare",args = {})
)

如何进行拦截的

就是在创建对象实例时生成代理类,采用责任链模式,由interceptorChain来进行生成代理对象并返回

复制代码
// Configuration 类中的方法

// 创建ParameterHandler
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

// 创建ResultSetHandler
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
    ResultHandler resultHandler, BoundSql boundSql) {
  ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  return resultSetHandler;
}

// 创建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

// 创建Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

工具类MetaObject

在编写插件的时候经常会使用到Mybatis提供的一个工具类MetaObject,可以有效地读取或修改一些重要对象的属性

复制代码
// 获取对象属性值,支持OGNL
 public Object getValue(String name)
// 修改对象属性值,支持OGNL
 public void setValue(String name, Object value)

https://zhhll.icu/2020/框架/mybatis/组件分析/11.mybatis拦截器/

本文由mdnice多平台发布

相关推荐
步行cgn10 分钟前
MyBatis 一对多关联映射详解
java·后端
用户31268748772015 分钟前
线程池到底怎么调?从源码看懂 ThreadPoolExecutor 的 7 个参数
java
SQL-First布道者37 分钟前
持久层框架的评价标准:只有一个
java·spring boot·spring·tomcat·mybatis·spring jdbc
孔明click3341 分钟前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·sa-token·开源·springboot·权限认证
m0_587383001 小时前
智慧场馆解决方案实战指南:从系统架构到落地部署全解析
java·spring boot·架构·系统架构
ZC跨境爬虫1 小时前
LeetCode 13. 罗马数字转整数(多解法详解 + Java Python 实现)
java·python·leetcode
省长1 小时前
Sa-Token v1.46.0 发布 🚀,来看看有没有令你心动的功能!
java·后端·开源
LayZhangStrive2 小时前
融360 一面
java·面试·后端开发
城管不管2 小时前
重生——第十次面试之开源中国一面挂
java·linux·开发语言·算法·面试·职场和发展·开源
Java小白笔记2 小时前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程