当前位置: 首页 > news >正文

SpringBoot整合Mybatis的核心原理

      • 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.Driverurl: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCusername: rootpassword: 123456mybatis:# mybatis-config.xml 配置文件的路径 与 configuration 不可一起设置#config-location: classpath:mapper/mybatis-config.xml# sql映射文件的位置mapper-locations: classpath:mapper/*.xml# 开启驼峰命名转化configuration:map-underscore-to-camel-case: truelog-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@ConditionalOnMissingBeanpublic 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的配置项赋值targetConfigurationif (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.");}// 创建DefaultSqlSessionFactoryreturn 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 {@Overridepublic 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类型的参数),进行数据库操作

相关文章:

SpringBoot整合Mybatis的核心原理

0. 前言:1. 自动配置类MybatisAutoConfiguration:1.1. SqlSessionFactory的生成:1.2. Mapper的扫描和代理生成:1.2.1. MapperScannerConfigurer1.2.2. MapperFactoryBean1.2.3. getMapper生成代理对象2. 小结:0. 前言&…...

滴滴一面:order by 调优10倍,思路是啥?

背景说明: Mysql调优,是大家日常常见的调优工作。 所以,Mysql调优是一个非常、非常核心的面试知识点。 在40岁老架构师 尼恩的读者交流群(50)中,其相关面试题是一个非常、非常高频的交流话题。 近段时间,有小伙伴面…...

Vue框架学习篇(五)

Vue框架学习篇(五) 1 组件 1.1 组件的基本使用 1.1.1 基本流程 a 引入外部vue组件必须要的js文件 <script src"../js/httpVueLoader.js"></script>b 创建.vue文件 <template><!--公共模板内容--></template><script><!…...

(蓝桥杯 刷题全集)【备战(蓝桥杯)算法竞赛-第1天(基础算法-上 专题)】( 从头开始重新做题,记录备战竞赛路上的每一道题 )距离蓝桥杯还有75天

&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6;&#x1f3c6; 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&a…...

C++——继承那些事儿你真的知道吗?

目录1.继承的概念及定义1.1继承的概念1.2 继承定义1.2.1定义格式1.2.2继承关系和访问限定符1.2.3继承基类成员访问方式的变化2.父类和子类对象赋值转换3.继承中的作用域4.派生类的默认成员函数5.继承与友元6. 继承与静态成员7.复杂的菱形继承及菱形虚拟继承如何解决数据冗余和二…...

leetcode 困难 —— N 皇后(简单递归)

&#xff08;不知道为啥总是给这种简单的递归设为困难题&#xff0c;虽然优化部分很不错&#xff0c;但是题目太好过了&#xff09; 题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个…...

AWS实战:Dynamodb到Redshift数据同步

AWS Dynamodb简介 Amazon DynamoDB 是一种完全托管式、无服务器的 NoSQL 键值数据库&#xff0c;旨在运行任何规模的高性能应用程序。DynamoDB能在任何规模下实现不到10毫秒级的一致响应&#xff0c;并且它的存储空间无限&#xff0c;可在任何规模提供可靠的性能。DynamoDB 提…...

机器学习评估指标的十个常见面试问题

评估指标是用于评估机器学习模型性能的定量指标。它们提供了一种系统和客观的方法来比较不同的模型并衡量它们在解决特定问题方面的成功程度。通过比较不同模型的结果并评估其性能可以对使用哪些模型、如何改进现有模型以及如何优化给定任务的性能做出正确的决定&#xff0c;所…...

常见的安全问题汇总 学习记录

声明 本文是学习2017中国网站安全形势分析报告. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 2017年重大网站安全漏洞 CVE-2017-3248 &#xff1a;WebLogic 远程代码执行 2017年1月27日&#xff0c;WebLogic官方发布了一个编号为CVE-2017-3248 的…...

元宵晚会节目预告没有岳云鹏,是不敢透露还是另有隐情

在刚刚结束的元宵节晚会上&#xff0c;德云社的岳云鹏&#xff0c;再一次参加并引起轰动&#xff0c;并获得了观众朋友们的一致好评。 不过有细心的网友发现&#xff0c;早前央视元宵晚会节目预告&#xff0c;并没有看到小岳岳&#xff0c;难道是不敢提前透露&#xff0c;怕公布…...

计算机视觉 吴恩达 week 10 卷积

文章目录一、边缘检测二、填充 padding1、valid convolution2、same convolution三、卷积步长 strided convolution四、三维卷积五、池化层 pooling六、 为什么要使用卷积神经网络一、边缘检测 可以通过卷积操作来进行 原图像 n✖n 卷积核 f✖f 则输出的图像为 n-f1 二、填充…...

JavaScript 函数定义

JavaScript 函数定义 函数是 JavaScript 中的基本组件之一。一个函数是 JavaScript 过程 — 一组执行任务或计算值的语句。要使用一个函数&#xff0c;你必须将其定义在你希望调用它的作用域内。 一个 JavaScript 函数用function关键字定义&#xff0c;后面跟着函数名和圆括号…...

设计模式:建造者模式教你创建复杂对象

一、问题场景 当我们需要创建资源池配置对象的时候&#xff0c;资源池配置类里面有以下成员变量: 如果我们使用new关键字调用构造函数&#xff0c;构造函数参数列表就会太长。 如果我们使用set方法设置字段值&#xff0c;那minIdle<maxIdle<maxTotal的约束逻辑就没地方…...

在C++中将引用转换为指针表示

在C中将引用转换为指针表示 有没有办法在c 中"转换"对指针的引用&#xff1f;在下面的例子,func2已经定义了原型和我不能改变它,但func是我的API,我想为pass两个参数,或一(组和第二组,以NULL)或既不(均设置为NULL): void func2(some1 *p1, some2 *p2); func(some1…...

PS快速入门系列

01-界面构成 1菜单栏 2工具箱 3工县属性栏 4悬浮面板 5画布 ctr1N新建对话框&#xff08;针对画布进行设置&#xff09; 打开对话框&#xff1a;ctrl0&#xff08;字母&#xff09; 画布三种显示方式切换&#xff1a;F 隐藏工具箱&#xff0c;工具属性栏&#xff0c;悬浮面板…...

ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程

ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程 我家里的MAC没这个问题。这个是在windows上发生的。 起因很简单我用ASP.NET CORE 3.1 MVC做个项目做登录将数据从VIEW post到Controller上结果意外的报了错误。 各种百度都说…...

JVM从看懂到看开Ⅲ -- 类加载与字节码技术【下】

文章目录编译期处理默认构造器自动拆装箱泛型集合取值可变参数foreach 循环switch 字符串switch 枚举枚举类try-with-resources方法重写时的桥接方法匿名内部类类加载阶段加载链接初始化相关练习和应用类加载器类与类加载器启动类加载器拓展类加载器双亲委派模式自定义类加载器…...

服务器常用的41个状态码及其对应的含义

服务器常用的状态码及其对应的含义如下&#xff1a; 100——客户必须继续发出请求 101——客户要求服务器根据请求转换HTTP协议版本 200——交易成功 201——提示知道新文件的URL 202——接受和处理、但处理未完成 203——返回信息不确定或不完整 204——请求收到&#…...

万里数据库加入龙蜥社区,打造基于“龙蜥+GreatSQL”的开源技术底座

近日&#xff0c;北京万里开源软件有限公司&#xff08;以下简称“万里数据库”&#xff09;及 GreatSQL 开源社区签署了 CLA&#xff08;Contributor License Agreement&#xff0c;贡献者许可协议&#xff09;&#xff0c;正式加入龙蜥社区&#xff08;OpenAnolis&#xff09…...

为什么不推荐使用CSDN?

CSDN粪坑 94%的讲得乱七八糟前言不搭后语互相矛盾的垃圾&#xff08;还包含直接复制粘贴其他源的内容&#xff09;3%的纯搬运&#xff08;偷窃&#xff09;2%个人日记 &#xff08;以上99%中还夹杂着很多明明都是盗版资源还要上传卖钱的 &#xff09; 1%黄金程序员时间有限&am…...

HuTool代理请求遇阻:深入解析HTTP/1.1 407 Proxy Authentication Required的成因与实战解决方案

1. 当HuTool遇上407&#xff1a;代理认证失败的典型场景 最近在项目中使用HuTool发送HTTPS请求时&#xff0c;突然遇到一个让人头疼的错误——HTTP/1.1 407 Proxy Authentication Required。这个错误就像高速公路上的收费站&#xff0c;明明已经交了通行费&#xff08;设置了代…...

AI赋能React开发:让快马智能助手帮你设计和优化复杂组件逻辑

AI赋能React开发&#xff1a;让快马智能助手帮你设计和优化复杂组件逻辑 最近在开发一个电商网站时&#xff0c;遇到了一个常见的需求&#xff1a;实现一个侧边栏商品筛选组件。这个组件需要包含价格区间滑块、多品牌复选框和分类下拉选择三个主要功能。刚开始觉得这个需求挺简…...

抖音音频提取工具 v1.0 - 快速提取抖音视频音频

抖音音频提取工具 v1.0 是可快速提取抖音短视频音频并保存本地的实用工具&#xff0c;依托 WebView2 与 FFmpeg 技术实现&#xff0c;操作简单易上手&#xff0c;能满足车机播放等个人娱乐音频使用需求&#xff0c;工具仅支持个人娱乐使用。抖音音频提取工具 v1.0 抖音短视频音…...

CodeSys WebVisu避坑指南:用three.js给机械臂做3D可视化,我踩过的8个坑

CodeSys WebVisu与three.js深度整合实战&#xff1a;机械臂3D可视化开发避坑手册 在工业自动化领域&#xff0c;机械臂的实时状态可视化一直是HMI开发中的难点与痛点。传统解决方案往往受限于渲染效果和交互灵活性&#xff0c;而基于WebGL的three.js技术栈恰好能弥补这些不足。…...

【架构师老王】AI真的在“杀死”软件吗?从系统烟囱到Agent时代的非侵入式重构

摘要 近期&#xff0c;“AI杀死软件”的论调在硅谷和国内技术圈闹得沸沸扬扬。作为一名在企业架构领域摸爬滚打15年的老兵&#xff0c;我见证了从单机版到SOA&#xff0c;再到微服务与云原生的每一次浪潮。客观来讲&#xff0c;AI杀死的并不是“软件”本身&#xff0c;而是那些…...

APISIX Dashboard实战:从零配置JWT认证网关(含Node.js后端对接)

APISIX Dashboard实战&#xff1a;从零构建JWT认证网关与Node.js后端深度集成 引言&#xff1a;为什么选择APISIX作为API网关&#xff1f; 在现代微服务架构中&#xff0c;API网关扮演着流量调度和安全防护的双重角色。APISIX作为云原生API网关的佼佼者&#xff0c;凭借其动态…...

闽北哥-做个无用之人,方成大用

做个无用之人 ——方成大用 “太有用的人&#xff0c;一定走不远。” &#x1f33f; 人生是一场‘无心生大用’的修行。 白木香树越能结香&#xff0c;越被千疮百孔&#xff1b; 无用之树&#xff0c;反得自然生长。 &#x1f4a1; 真正的价值&#xff0c;不在“有”&#xff…...

小红书数据采集系统深度探索:从技术原理到实战落地

小红书数据采集系统深度探索&#xff1a;从技术原理到实战落地 【免费下载链接】XiaohongshuSpider 小红书爬取 项目地址: https://gitcode.com/gh_mirrors/xia/XiaohongshuSpider 在当今数据驱动的时代&#xff0c;小红书作为内容丰富的社交平台&#xff0c;其数据价值…...

游戏玩家如何选?网易UU/ToDesk远程控制延迟实测(含手机投屏技巧)

游戏玩家专属远程控制工具深度评测&#xff1a;延迟、画质与投屏技巧全解析 作为一名资深游戏玩家&#xff0c;你是否遇到过这样的场景&#xff1a;出差在外想用手机继续刷副本&#xff0c;却苦于找不到合适的远程控制方案&#xff1b;或是想在平板上玩PC独占的3A大作&#xff…...

不是删改,是升级:百考通智能降重+降AI,让语言更学术、更像“你”

在一个人工智能可以生成论文的时代&#xff0c;最荒诞的现实不是机器冒充人类&#xff0c; 而是人类因写得太像“一篇合格的学术论文”&#xff0c;被当作AI。 2026年&#xff0c;无数普通学子正陷入一场无声的困境&#xff1a; 你没用任何代写&#xff0c;却因逻辑清晰被系统…...