聊聊mybatis的Interceptor机制

这篇具有很好参考价值的文章主要介绍了聊聊mybatis的Interceptor机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文主要研究一下mybatis的Interceptor机制

Interceptor

org/apache/ibatis/plugin/Interceptor.java

public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  default void setProperties(Properties properties) {
    // NOP
  }

}

Interceptor定义了intercept方法,其参数为Invocation类型,同时默认提供了plugin方法,通过Plugin.wrap(target, this)进行包装

Invocation

org/apache/ibatis/plugin/Invocation.java

public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }

}

Invocation定义了target、method、args属性,提供了proceed方法则是反射执行method方法

Plugin

org/apache/ibatis/plugin/Plugin.java

public class Plugin implements InvocationHandler {

  private final Object target;
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  @Override
  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)) {
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  //......
}  

Plugin实现了java.lang.reflect.InvocationHandler方法,其invoke方法主要是多了一层判断,判断interceptor的signatureMap有没有包含对应的方法,有则执行interceptor.intercept,同时包装了Invocation参数传递过去
而Plugin的wrap方法则是判断interceptor有没有拦截target对应的接口,如果有则通过Proxy.newProxyInstance返回代理对象方便后续进行拦截

InterceptorChain

org/apache/ibatis/plugin/InterceptorChain.java

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

InterceptorChain定义了interceptors,它提供了pluginAll方法对target代理所有的interceptor

Configuration

org/apache/ibatis/session/Configuration.java

  protected final InterceptorChain interceptorChain = new InterceptorChain();

  public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

Configuration定义了interceptorChain,它通过addInterceptor方法往interceptorChain添加interceptor

XMLConfigBuilder

org/apache/ibatis/builder/xml/XMLConfigBuilder.java

  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor()
            .newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

XMLConfigBuilder在解析xml的plugin的时候,会获取定义的interceptor,实例化之后通过configuration.addInterceptor添加进去

SqlSessionFactoryBean

org/mybatis/spring/SqlSessionFactoryBean.java

  private Interceptor[] plugins;

  public void addPlugins(Interceptor... plugins) {
    setPlugins(appendArrays(this.plugins, plugins, Interceptor[]::new));
  }

  public void setPlugins(Interceptor... plugins) {
    this.plugins = plugins;
  }

  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
      LOGGER.debug(
          () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

    //......

    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }

    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
          .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }

    targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);

    //......

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

SqlSessionFactoryBean的buildSqlSessionFactory方法在判断plugins不为空时,通过targetConfiguration.addInterceptor(plugin)将interceptor注册进去

MybatisAutoConfiguration

org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {

  private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

  private final MybatisProperties properties;

  private final Interceptor[] interceptors;

  //......

  public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
      ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
      ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider,
      ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers) {
    this.properties = properties;
    this.interceptors = interceptorsProvider.getIfAvailable();
    this.typeHandlers = typeHandlersProvider.getIfAvailable();
    this.languageDrivers = languageDriversProvider.getIfAvailable();
    this.resourceLoader = resourceLoader;
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
    this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable();
  }

  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    applyConfiguration(factory);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }

    //......
  }  

MybatisAutoConfiguration的sqlSessionFactory方法,在判断interceptors不为空时,通过SqlSessionFactory的setPlugins方法把interceptors添加进去;MybatisAutoConfiguration标注了@Configuration注解,该注解标注了@Component,因而这些interceptors则是通过构造器从spring中注入的

Configuration.pluginAll

org/apache/ibatis/session/Configuration.java

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

  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);
    return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  }

  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);
    return (StatementHandler) interceptorChain.pluginAll(statementHandler);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : 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);
    }
    return (Executor) interceptorChain.pluginAll(executor);
  }

Configuration提供了newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor方法,这些方法会对ParameterHandler、ResultSetHandler、StatementHandler、Executor执行interceptorChain.pluginAll方法,则创建作用了所有interceptor的代理对象,从而实现对这些对象的拦截效果文章来源地址https://www.toymoban.com/news/detail-682581.html

小结

  • mybatis的Interceptor机制使用的是jdk的Proxy.newProxyInstance的方式
  • 在扫描xml的时候把interceptor注册到configuration中,针对spring的场景,在MybatisAutoConfiguration中注入所有托管的interceptor,之后在构造SqlSessionFactory的时候把interceptor注册到configuration中
  • 最后Configuration提供了newParameterHandler、newResultSetHandler、newStatementHandler、newExecutor方法,创建作用了所有interceptor的代理对象,从而实现对这些对象的拦截效果

到了这里,关于聊聊mybatis的Interceptor机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 聊聊Redis sentinel 机制

    Redis 的哨兵机制自动完成了以下三大功能,从而实现了主从库的自动切换,可以降低 Redis 集群的运维开销: 监控主库运行状态,并判断主库是否客观下线; 在主库客观下线后,选取新主库; 选出新主库后,通知从库和客户端。   主从模式下,如果主库发生故障了,那就直

    2023年04月16日
    浏览(24)
  • 【数据库】聊聊MVCC机制与BufferPool缓存机制

    上一篇文章,介绍了隔离级别,MySQL默认是使用可重复读,但是在可重复读的级别下,可能会出现幻读,也就是读取到另一个session添加的数据,那么除了配合使用间隙锁的方式,还使用了MVCC机制解决,保证在可重复读的场景下,同一个session读取的数据一致性。 MVCC(Multi-Vers

    2024年01月20日
    浏览(32)
  • 在?聊聊浏览器事件循环机制

    目录 前言  同步/异步编程模型 同步 异步 JS异步模型 调用栈 任务队列 宏任务队列 微任务队列 微任务API 事件循环 队列优先级 混合队列 事件循环实现 总结 参考文章 Event-Loop可视化工具 JS是单线程语言,在某个时间段只能执行一段代码。这种单线程模型的好处是不会出现多

    2024年02月10日
    浏览(55)
  • 聊聊mybatis-plus的SafetyEncryptProcessor

    本文主要研究一下mybatis-plus的SafetyEncryptProcessor mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/SafetyEncryptProcessor.java SafetyEncryptProcessor实现了EnvironmentPostProcessor接口,在postProcessEnvironment方法中先是找到mpw.key,然后遍历所有PropertySource的所有属性,找到mpw:开头的,然

    2024年02月10日
    浏览(22)
  • 【操作系统】聊聊Linux内存工作机制

    内存主要是用来存储系统和应用程序的指令、数据、缓存等 内存是需要安全机制保护的,所以只有内核才可以直接访问物理内存。进程如果要访问内存需要通过独立的虚拟地址空间。 虚拟地址空间其实包含两部分。一部分是内核空间,另一部分就是用户空间。 进程有用户态

    2024年02月09日
    浏览(32)
  • 聊聊ElasticeSearch并发写的乐观锁机制

    ES的多客户端并发更新是基于乐观并发控制,通过版本号机制来实现冲突检测。 ES的老版本是用过 _version 字段的版本号实现乐观锁的。现在新版增加了基于 _seq_no 与 _primary_term 字段,三个字段做乐观锁并发控制。 _version :标识文档的版本号,只有当前文档的更新,该字段才会

    2024年02月06日
    浏览(28)
  • 聊聊mybatis-plus的sql加载顺序

    本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题 com/baomidou/mybatisplus/core/MybatisConfiguration.java com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法会根据mapperLocations的配置取加载xml配置,即加载xml的mapper信息

    2024年02月10日
    浏览(29)
  • MyBatis第八讲:MyBatis插件机制详解与实战

    MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能。那么拦截器拦截MyBatis中的哪些内容呢?本文是MyBatis第8讲,对MyBatis插件机制详解。

    2024年02月13日
    浏览(44)
  • 【MyBatis】五、MyBatis的缓存机制与逆向工程

    MyBatis的一级缓存是默认开启的,是基于SqlSession级别的缓存,也就是说,只要是同一个sqlSession,只要执行的语句相同,则不会去数据库中进行查找,而是会从缓存中找到对应的结果。 使用了不同的sqlsession对象 同一个sqlsession对象,但查询条件不同 两次查询之间进行了增删改

    2024年02月09日
    浏览(31)
  • MyBatis第十一讲:MyBatis事务管理机制详解

    本文是MyBatis第十一讲,主要介绍MyBatis事务管理相关的使用和机制。

    2024年02月16日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包