SpringBoot整合Mybatis的核心原理

news2024/11/26 12:28:10

      • 0. 前言:
      • 1. 自动配置类MybatisAutoConfiguration:
        • 1.1. SqlSessionFactory的生成:
        • 1.2. Mapper的扫描和代理生成:
          • 1.2.1. MapperScannerConfigurer
          • 1.2.2. MapperFactoryBean
          • 1.2.3. getMapper生成代理对象
      • 2. 小结:

0. 前言:

  • SpringBoot整合Mybatis只需添加mybatis-spring-boot-starter的依赖(本文版本2.2.0,对应mybatis版本3.5.7),然后yml进行配置即可
  • 本文对Mybatis一些底层原理进行探究,主要是一些自动配置以及Mapper代理对象的生成过程
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    
mybatis:
  # mybatis-config.xml 配置文件的路径 与 configuration 不可一起设置
  #config-location: classpath:mapper/mybatis-config.xml
  # sql映射文件的位置
  mapper-locations: classpath:mapper/*.xml
  # 开启驼峰命名转化
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 开启别名
  type-aliases-package: com.example.demo.easy.domain

1. 自动配置类MybatisAutoConfiguration:

1.1. SqlSessionFactory的生成:

  • 其中上述yml的mybatis配置项会被读取封装到properties里,通过SqlSessionFactoryBean来构建SqlSessionFactory
  @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()));
    }
  
 	// ... ... 省略一些赋值,详细可看源码
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
   }   
    return factory.getObject();
  }

在这里插入图片描述

org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory

  • 过程中会将mybatis的配置被解析封装成Configuration对象(yml的配置项会被mybatis-config.xml的配置项覆盖)
  • mapper.xml会被解析封装成MappedStatement对象(用于存储要映射的SQL语句的id、参数等信息)
  • 最终会通过this.sqlSessionFactoryBuilder.build(targetConfiguration);去new一个DefaultSqlSessionFactory
 protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configuration != null) {
 		... ...
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
 		... ...
    }
	// ... ... yml的配置项赋值targetConfiguration
    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 (xmlConfigBuilder != null) {
      try {
      	// 解析mybatis-config.xml配置项并会覆盖yml的配置
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }
	... ...
    if (this.mapperLocations != null) {
      	  ... ...
          try {
          	// mapper.xml会被解析封装成MappedStatement对象(用于存储要映射的SQL语句的id、参数等信息)
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } 
          ... ...
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }
	// 创建DefaultSqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

1.2. Mapper的扫描和代理生成:

  • 如果没有使用@MapperScan和手动配置过MapperFactoryBean、MapperScannerConfigurer,默认会扫描启动类所在包路径
  • MapperScannerRegistrarNotFoundConfiguration 这个bean还Import了AutoConfiguredMapperScannerRegistrar
  • AutoConfiguredMapperScannerRegistrar实现了ImportBeanDefinitionRegistrar扩展接口,容器启动时会执行registerBeanDefinitions方法
  • 这个注册器中会定义MapperScannerConfigurer的BeanDefinition,通过addPropertyValue来对内部属性赋值,然后进行注册
  @org.springframework.context.annotation.Configuration
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }

  }

在这里插入图片描述

1.2.1. MapperScannerConfigurer
  • 实现了BeanDefinitionRegistryPostProcessor扩展接口,容器启动会执行postProcessBeanDefinitionRegistry方法
  • 引入类路径Mapper扫描器ClassPathMapperScanner,调用scan方法(最终调用doScan方法)进行Mapper接口扫描

在这里插入图片描述

org.mybatis.spring.mapper.ClassPathMapperScanner#doScan

  • ClassPathMapperScanner继承ClassPathBeanDefinitionScanner,调用父类方法来扫描包路径下Mapper
  • 父类的扫描器是Spring定义的,有其自身的扫描规则,最终会将Mapper接口扫描封装到BeanDefinition中
  • 由于Mapper接口是没有实现类的,如果不做处理是无法生成Bean然后放入IOC容器使用的
  • 所以要对BeanDefinition的beanClass做修改,修改成一个MapperFactoryBean,见processBeanDefinitions方法处理

在这里插入图片描述
在这里插入图片描述

1.2.2. MapperFactoryBean
  • 上文修改之后相当于beanDefinitionMap中(mapper,持有mapperClass的MapperFactoryBean的BeanDefinition)
  • MapperFactoryBean实现了FactoryBean,在Bean生命周期管理时会调用getObject方法
  • getObject方法中通过getMapper获取Mapper的代理对象

在这里插入图片描述

1.2.3. getMapper生成代理对象
  • 最终通过MapperProxyFactory来创建Mapper的代理对象MapperProxy,可以看出采用的jdk动态代理
  • 所以最终启动后IOC容器的Map储存(mapper,MapperProxy),通过DI进行注入MapperProxy使用
  • MapperProxy是实现InvocationHandler的,最终调用时会触发代理对象的invoke方法

在这里插入图片描述
在这里插入图片描述

  • MapperProxy的invoke方法就不介绍了
  • 最终会通过SqlSessionFactor 创建的SqlSession去调用Executor执行器(入参:MappedStatement类型的参数),进行数据库操作

2. 小结:

  • 自动配置时,会将mybatis的配置被解析封装成Configuration对象
  •  mapper.xml也会被解析封装成MappedStatement对象(用于存储要映射的SQL语句的id、参数等信息)
  •  然后通过this.sqlSessionFactoryBuilder.build(Configuration)去创建SqlSessionFactor
  • 自动配置的过程中会通过ClassPathMapperScanner扫描器找到Mapper接口,封装成各自的BeanDefinition
  • 然后循环遍历对Mapper的BeanDefinition修改beanClass为MapperFactoryBean
  • MapperFactoryBean实现了FactoryBean,在Bean生命周期管理时会调用getObject方法,通过jdk动态代理生成代理对象MapperProxy
  • Mapper接口请求的时候,执行MapperProxy代理类的invoke方法,执行的过程中通过SqlSessionFactor 创建的SqlSession去调用Executor执行器(入参:MappedStatement类型的参数),进行数据库操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue2仿网易云风格音乐播放器(附源码)

Vue2仿网易云风格音乐播放器1、整体效果2、使用技术3、实现内容4、源码5、使用图片1、整体效果 2、使用技术 使用了HTML5 CSS3进行页面布局及美化使用Vue2进行数据渲染与页面交互使用Axios发送http请求获取数据 3、实现内容 实现了搜索歌曲功能,输入歌手或歌曲关…

如果企业遭受到攻击应该进行怎样的处理

声明 本文是学习2018勒索病毒白皮书政企篇. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 政企遭遇勒索攻击分析 由于感染政企客户更有可能获得赎金,再加上勒索病毒本身也以服务器定向攻击为主,所以,2018年政企客…

构建工具tsup入门第三部分

🎄Hi~ 大家好,我是小鑫同学,一位长期从事前端开发的编程爱好者,我将使用更为实用的案例输出更多的编程知识,同时我信奉分享是成长的唯一捷径,在这里也希望我的每一篇文章都能成为你技术落地的参考~ 目录&am…

“慌不择路”周鸿祎,昔日大炮忙跟风【短评】

文|智能相对论作者| 凯文2月7日下午360经历两次急速拉升后涨停了,作为一个被套牢的股民,我是羡慕的,但理智告诉我,360的后续难以为继。360涨停的原因很简单,只因其在投资者互动平台上对类ChatGPT技术的布局做出了回应&…

安装Sentinel控制台与初始化演示工程

目录 一、Sentinel 二、安装Sentinel控制台 (一)sentinel组件由2部分构成 (二)安装步骤 三、初始化演示工程 四、流控模式 (一)快速失败 (二)关联资源 (三&…

第四章——随机变量的数字特征

文章目录1、数字特征的定义2、数学期望(均值)2.1、数学期望的定义及性质2.1.1、定义2.1.2、性质2.2、数学期望相关例题2.3、Yg(X)的数学期望2.4、Zg(X,Y)的数学期望2.5、随机变量函数的数学期望例题3、方差3.1、方差的定义与性质3.2、相关例题3.3、切比雪…

3.2 埃尔米特转置

定义 对于复矩阵,转置又不一样,常见的操作是共轭转置,也叫埃尔米特转置Hermitian transpose。埃尔米特转置就是对矩阵先共轭,再转置,一般来说用三种符号表示埃尔米特转置: 第一种符号是AHA^HAH&#xff0c…

热门盘点 | 10款评分最高的项目管理工具

项目管理软件可以让项目经理及时掌握项目进展可把复杂的任务分解简单帮助项目经理及时了解整个团队进展随着现代项目需求日趋复杂和个性选一个好的项目管理软件还是很有必要的① PingCode国内研发项目管理软件PingCode,它是国内软件研发项目榜单中评分最高的项目管理…

达梦实现高可用性的实现(failover功能/负载均衡/虚拟ip透明切换)

达梦实现高可用性的实现(failover功能/负载均衡/虚拟ip透明切换)一:failover功能:基于守护进程和监视器两个内在工具实现守护进程监视器:数据守护和读写分离集群共享存储集群二:负载均衡:基于jd…

在线支付系列【22】微信支付实战篇之集成服务商API

有道无术,术尚可求,有术无道,止于术。 文章目录前言1. 环境搭建2. 特约商户进件3. 统一下单总结前言 在上篇文档中,我们做好了接入前准备工作,接下来使用开源框架集成服务商相关API。 一个简单的支付系统完成支付流程…

火爆全网的ChatGPT使用教程

最近,大家有没有被ChatGPT所刷屏呢?记得之前元宇宙刚出来的时候,也是极其火爆,虽说这二者是不同的性质,但是都是代表着当下互联网发展之迅速。 一、什么是ChatGPT 百度百科中给出的解释:ChatGPT&#xff0…

【Explain详解与索引优化最佳实践】

摘要 explain命令是查看MySQL查询优化器如何执行查询的主要方法,可以很好的分析SQL语句的执行情况。每当遇到执行慢(在业务角度)的SQL,都可以使用explain检查SQL的执行情况,并根据explain的结果相应的去调优SQL等。 …

LabVIEW中加载.NET 2.0,3.0和3.5程序集

LabVIEW中加载.NET 2.0,3.0和3.5程序集已使用.NETFramework 2.0,3.0或3.5创建了.NET程序集,但是当尝试在构造函数节点中加载这些程序集时,却收到LabVIEW消息显示: 所选文件不是.NET程序集,所属类型库或自动化可执行文件。所以想确认是否可以在…

多线程环境下的伪共享

今天和大家聊一聊伪共享 1.什么是伪共享? 缓存一致性协议在计算机中针对的最小单元:缓存行,每个缓存行的大小是64字节,一串连续的64字节数据都会存储到缓存行中。 假设数据A和数据B在同一缓存行中,CPU1修改了数据A&am…

你每天所做的工作,让你产生了成就感吗?

我们是为了什么而工作?金钱?理想?生活? 似乎这一切都没有标准答案,你自己问你自己,问问你自己,每天踏入公司,坐到工位面前,你最真实的感受是什么? “成就感…

OpenHarmony 3.2 Beta多媒体系列——视频录制

一、简介媒体子系统为开发者提供了媒体相关的很多功能,本文针对其中的视频录制功能做个详细的介绍。首先,我将通过媒体子系统提供的视频录制Test代码作为切入点,给大家梳理一下整个录制的流程。二、目录foundation/multimedia/camera_framewo…

今天面了个字节拿 38K 出来的测试,让我见识到了基础的天花板

前言 人人都有大厂梦,对于软件测试人员来说,BAT 为首的一线互联网公司肯定是自己的心仪对象,毕竟能到这些大厂工作,不仅薪资高待遇好,而且能力技术都能够得到提升,最关键的是还能够给自己镀上一层金&#…

webpack新手入门

前言: 如何配置webpack呢? webpack概念有哪些呢? 怎么快速理解并使用webpack呢? 文章目录一. 什么是webpack二. 安装webpack三. webpack的五个核心概念四. webpack配置五. loader加载器1. css处理2. 处理文件(图片&…

内网渗透(十二)之内网信息收集-内网端口扫描和发现

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

FOXCONN的第一天 记录 转型的前奏

2023年春,我来到美丽富饶的威海小城,开始了我的foxconn之旅,刚毕业那阵来过之后,再一次参与这个价值10个亿的项目,首先要致敬郭老板,好久不见。然后致敬一下曾经的工友们,再次致敬一下逝去的SAP…