spring5源码篇(9)——mybatis-spring整合原理

这篇具有很好参考价值的文章主要介绍了spring5源码篇(9)——mybatis-spring整合原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

spring-framework 版本:v5.3.19

spring和mybatis的整合无非主要就是以下几个方面:
1、SqlSessionFactory怎么注入?
2、Mapper代理怎么注入?
3、为什么要接管mybatis事务?

一、SqlSessionFactory怎么注入

SqlSessionFactoryBean

在mybatis-spring中,定义了一个新的factoryBean——SqlSessionFactoryBean,我们将其注入到spring容器即可。
spring5源码篇(9)——mybatis-spring整合原理
其实也不难猜到,这个factoryBean的getObject肯定返回了一个SqlSessionFactory,这样spring容器中就有一个SqlSessionFactory bean了。后续就跟使用原生mybatis一样,通过这个SqlSessionFactory去生成SqlSession进而生成Mapper的代理类去操作数据库。
所以getObject方法是如何生成SqlSessionFactory?来到buildSqlSessionFactory

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 (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
          .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }

    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }

    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);

    if (!isEmpty(this.scriptingLanguageDrivers)) {
      Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
        targetConfiguration.getLanguageRegistry().register(languageDriver);
        LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
      });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver)
        .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

    if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new IOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new IOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));

    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

跟原生mybatis一样,构建一个Configuration对象,然后使用SqlSessionFactoryBuilder去build一个SqlSessionFactory。需要注意的是,这个过程中就算外界传入的Configuration对象有环境、数据源、事务管理器的配置(无论是xml配置还是代码)都会被忽略,取而代之的是SqlSessionFactoryBean对应的environment、dataSource、transactionFactory属性。至此spring中就相当于有了SqlSessionFactory bean,后续spring就可以用这个SqlSessionFactory bean去生成SqlSession。

二、Mapper代理怎么注入

MapperFactoryBean

跟使用原生mybatis一样,对mapper接口生成一个个的代理对象。即在整合过程中会生成一个个代理对象bean,对应流程如下。
spring5源码篇(9)——mybatis-spring整合原理
1、MapperScan注解import了MapperScannerRegistrar bean。
2、MapperScannerRegistrar是一个ImportBeanDefinitionRegistrar会注册MapperScannerConfigurer bean。
3、MapperScannerConfigurer是一个BeanFactoryPostProcessor(BeanDefinitionRegistryPostProcessor),对应的增强方法会扫描出指定路径下的Mapper,并把class type设为MapperFactoryBean。
4、MapperFactoryBean是一个FactoryBean,其getObject方法跟原生mybatis一样通过SqlSession.getMapper生成一个mapper代理对象,然后将其注入spring容器。

SqlSessionTemplate

所以是直接使用SqlSession来生成mapper的代理对象吗?其实不然,实际MapperFactoryBean的getObject中的getSqlSession()返回的是一个SqlSessionTemplate
spring5源码篇(9)——mybatis-spring整合原理
SqlSessionTemplate中维护了一个SqlSession的代理对象,后续有关数据库的操作都会经过这个代理对象。
spring5源码篇(9)——mybatis-spring整合原理
代理SqlSession的逻辑大概就是,如果是同一个事务就获取同一个SqlSession,如果不是同一个事务就新建一个SqlSession,然后再用这个SqlSession去做事情。这么做是为了解决SqlSession线程不安全的问题,正是因为如此导致不同事务用不同的SqlSession,所以一级缓存也就失效了。

三、为什么要接管mybatis事务

SpringManagedTransactionFactory

在SqlSessionFactoryBean的buildSqlSessionFactory中,我们默认会使用SpringManagedTransactionFactory作为我们的事务管理器来接管mybatis事务。
spring5源码篇(9)——mybatis-spring整合原理
这么做是为了让 MyBatis 的事务管理与 Spring 的事务管理能够协同工作,从而实现 MyBatis 与 Spring 的事务管理一致性
具体来说,SpringManagedTransactionFactory 在创建事务时,会获取 Spring 的事务管理器,并将其封装为 MyBatis 的 Transaction 对象。在事务提交或回滚时,SpringManagedTransactionFactory 会将事务的状态同步到 Spring 的事务管理器中。文章来源地址https://www.toymoban.com/news/detail-435408.html

到了这里,关于spring5源码篇(9)——mybatis-spring整合原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【官方中文文档】Mybatis-Spring #搭配 Spring Boot

    请查看 MyBatis Spring-boot-starter 子项目获取更多信息。

    2024年02月11日
    浏览(12)
  • 【官方中文文档】Mybatis-Spring #注入映射器

    与其在数据访问对象(DAO)中手工编写使用 SqlSessionDaoSupport 或 SqlSessionTemplate 的代码,还不如让 Mybatis-Spring 为你创建一个线程安全的映射器,这样你就可以直接注入到其它的 bean 中了: 注入完毕后,映射器就可以在你的应用逻辑代码中使用了: 注意代码中并没有任何的对

    2024年02月11日
    浏览(9)
  • 【官方中文文档】Mybatis-Spring #使用 SqlSession

    在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession 。 一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 使用 MyBatis-Spring 之后,你不再需要直接使用 SqlSessionFactory 了,因为你的 bean 可以被

    2024年02月11日
    浏览(15)
  • Spring5学习随笔-Spring5的第一个程序(环境搭建、日志框架整合)

    Spring5学习随笔-Spring5的第一个程序(环境搭建、日志框架整合)

    学习视频:【孙哥说Spring5:从设计模式到基本应用到应用级底层分析,一次深入浅出的Spring全探索。学不会Spring?只因你未遇见孙哥】 1.JDK1.8+ 2.Maven3.5+ 3.IDEA2018+ 4.SpringFramework 5.1.4 官网:www.spring.io Spring的jar包 1.设置pom的依赖 Spring的配置文件 1.配置文件的放置位置:任意位置

    2024年02月05日
    浏览(29)
  • SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis

    SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring Data是一个用于简化数据库访问和操作的开源框架,为开发人员提供了一种通用的方式来处理不同类型的数据存储,例如关系型数据库(如MySQL、PostgreSQL、Oracle)和非关系型数据库(如MongoDB、Cassandra、Redis)等。

    2024年02月12日
    浏览(29)
  • 【Spring+SpringMVC+Mybatis】SSM框架的整合、思想、工作原理和优缺点的略微讲解

    【Spring+SpringMVC+Mybatis】SSM框架的整合、思想、工作原理和优缺点的略微讲解

    🚀欢迎来到本文🚀 🍉个人简介:陈童学哦,目前学习C/C++、算法、Python、Java等方向,一个正在慢慢前行的普通人。 🏀系列专栏:陈童学的日记 💡其他专栏:C++STL,感兴趣的小伙伴可以看看。 🎁希望各位→点赞👍 + 收藏⭐️ + 留言📝 ​ ⛱️万物从心起,心动则万物动🏄

    2024年02月10日
    浏览(13)
  • 答疑解惑:解释在Mybatis-Spring下@Mapper与@MapperScan为何不能同时生效以及实现动态条件注册Mapper接口

    若项目中使用了@MapperScan注解后,则@Mapper注解不再生效 , 原因是:@MapperScan注解 会执行@Import(MapperScannerRegistrar.class),而MapperScannerRegistrar又会注册MapperScannerConfigurer BEAN,在MapperScannerConfigurer BEAN中会完成基于配置的包目录扫描注册所有mapper interface代理BEAN,而@Mapper注解的生效

    2024年02月11日
    浏览(10)
  • spring5源码篇(12)——spring-mvc请求流程

    spring5源码篇(12)——spring-mvc请求流程

    spring-framework 版本:v5.3.19 总体流程在 DispatchServelt#doDispatch 方法 首先会获取根据url去映射对应的处理器(即接口执行方法) 看到对应的 getHandler 方法 为方便阅读,进入debug。可以看到springmvc默认为我们注册了三个handlerMapping。 springMvc中的各个组件如处理器映射器,处理器适配

    2024年02月15日
    浏览(10)
  • Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

    Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

    开篇: 欢迎再次来到 Spring 5 学习系列!在这个博客中,我们将深入研究 Spring 框架的AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式)。 概念 什么是AOP (1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的

    2024年01月24日
    浏览(16)
  • Spring整合Mybatis、Spring整合JUnit

    Spring整合Mybatis、Spring整合JUnit

    🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaweb 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 1.3.1jdbc配置文件 1.3.2jdbc配置类及spring配置类 1.3.3数据库操作类 1.3.4测试类 1.3.5运行结果 关于Spring整合的相关步骤就介绍完了,欢迎各位点赞+关注!!!

    2024年02月14日
    浏览(42)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包