首页 > 编程语言 >窥探Mybatis配置到执行源码剖析

窥探Mybatis配置到执行源码剖析

时间:2024-06-20 15:12:34浏览次数:22  
标签:mapper 解析 里面 窥探 源码 sql Mybatis 执行 方法

mybatis自动配置过程

首先我们项目中使用mybatis如果是mybatis的话会引入依赖

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

引入这个starer,想要窥探,先从starer入手,SpringBoot自动配置会寻找spring.factories这个文件,但是这是springboot 2.7.0之前的事情了,2.7.0之后引入了org.springframework.boot.autoconfigure.AutoConfiguration.imports来代替spring.factories自动配置注册方式, 一个位于META-INF/spring目录下,作为starer这个功能是向后兼容的

首先从MybatisAutoConfiguration看起

首先明确这个一个配置类,并且实现了InitializingBean,并且加载了MybatisProperties这个主要的配置类,
1、其次开始调用的是有参构造方法,其中ObjectProvider也是DI的一部分,在处理延迟加载,可选依赖,以及获取多个实例方面非常有用,比如下面处理拦截器 interceptorsProvider.getIfAvailable(),如果未配置或没找到这个bean,则返回null,可以安全的处理可选依赖,对于一个框架来说,有很多的配置属性,并不是所有都能用上的,所有使用ObjectProvider无非是上上之策

2、调用完属性值set开始执行InitializingBean的afterPropertiesSet方法了,
可以发现如果checkConfigLocation:true的话判断是否存在configLocation属性的配置文件,实际中没什么卵用,

3、因为没有别的依赖关系,接下来开始注入的是SqlSessionFactory这个bean,但是这个bean是通过FactoryBean来完成的,并且是单例模式, 构造方法是简单粗暴的new SqlSessionFactoryBean()方法,并且依赖与刚才的属性MybatisProperties,以及Interceptor[],

但是在SqlSessionFactoryBean类里面由于实现了InitializingBean以及FactoryBean,所以核心功能是构造SqlSessionFactory,除了构造SqlSessioFactory还有一个重要的功能,就是解析*mapper.xml文件,解析xml的逻辑看似很复杂,其实一点也不简单,最终返回DefaultSqlSessionFactory

4、根据加载顺序,下一个是创建SqlSessionTemplate这个bean

SqlSessionTemplate这个类里面主要的可能就是创建了一个SqlSession的代理类,这个代理是jdk代理类,

5、下面就是开始扫描注册所有的mapper类了

我们知道注入spring bean的时候可以使用@Bean或者@Component等方法一一进行显式的注入,但是当有大量相同类似的bean时可以借助@Import进行注入,这里通过@Import + ImportBeanDefinitionRegistrar 通过BeanDefinition可以更加灵活的注入,以及自定义配置,也是常用的手段,像Feign也是采用这种方式进行扫描注入

但是这个地方看名字MapperScannerRegistrar,通过
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);可以看到其实首先注入的是MapperScannerConfigurer这个类,它才是真实扫描basePackage来进行注入mapper的
这里需要注意的是,以mybatis开头的这几个属性都是配置文件中的内容,区别在于lazyInitialization和defaultScope使用了占位符,而inject-sql-session-on-mapper-scan在后续的逻辑中需要用这个属性的值,所以使用environment直接获取属性的值

仔细看defaultScope的值为\({mybatis.mapper-default-scope:},他跟\){mybatis.mapper-default-scope}又有何区别呢,

这里举个例子,custom.prop和custom.defaultProp这两个属性值在配置文件中都不存在,最后启动项目会发现根本起不来,如果将\({custom.prop}换成\){custom.prop:}在PropertyPlaceholderHelper中发现会将:后面的设置为默认值,所以这里custom.prop默认值为"",


注册完MapperScannerConfigurer内部是通过实现BeanDefinitionRegistryPostProcessor通过annotationClass也就是Mapper.class来扫描
在ClassPathMapperScanner中,根据父类ClassPathBeanDefinitionScanner进行doScan进行扫描Mapper.class的类,这里bean的定义信息还是处于BeanDefinition没有实例化状态,这个中间态我们可以大做文章,动态的修改bean的元数据信息,当调用父类进行扫描完之后,我们开始执行此类中的processBeanDefinitions来进行其他属性的赋值,当然BeanDefinition最重要的方法还是setBeanClass,如果是提前知道类型可以使用BeanDefinitionBuilder.genericBeanDefinition’
,beanClass将是即将实例化的类型,这里beanClass为MapperFactoryBean由于实现了FactoryBean,但是getObjects实际返回的是mapperInterface,所以其实@Autowired private XXXMapper xxxmapper其实是在这个地方注入的
MapperBeanFactory实现了InitializingBean,所以每个mapper后续的逻辑从这里进行

addToConfig默认值为true。这里面主要的逻辑就是将mapper交给Configurationl里面的mapperRegistry进行维护处理


内部是一个Map<Class, MapperProxyFactory>的一个map,
其中在addMapper的时候,会new一个MapperProxyFactory放入map,通过名字也可以看出来这是mapper类的一个代理工厂,主要方法为创建代理类newinstance

同时在addMapper中有一个非常重要的东西,看名字是解析注解类的,但是其实parse方法里面也包含了parseCache、parseCacheRef、parseResultMap、parseStatement,因为sql结果返回的类型,执行sql的类型,这些都是固定的,而且只需要执行一次,所以在这个时候提前执行可以避免不必要的错,只有sql的参数是在后续逻辑执行sql之前进行解析的

但是可以看到parse.parse()并没有返回结果,所以里面肯定是有猫腻,大概是借助第三个类将解析完的结果存了起来,最后发现有一个助理类MapperBuilderAssistant来帮助完成这些功能,(Mybatis plus里面的baseMapper也是借助这个)当然最后的存储都是放到了Configuration里面,parse方法里面比较重要的应该就是parseStatement这个了,如果有解析失败的,会放进Configurations的incompleteMethods里面,等待所有的方法都执行完了,会进行重试,这里有的项目可能用的多,有的可能用的不是很多,这个方法主要解析带有这些注解的方法Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,InsertProvider.class, DeleteProvider.class,
有的喜欢在@SelectProvier里面写大篇sql的这个地方会执行耗时长一些,有的喜欢xml写的 loadXmlResource 这方法会耗时长一些

解析xml文件的逻辑也比较复杂,这里先不看了,

解析的时候,不管是注解形式的还是xml形式的,最后都是通过助理类MapperBuilderAssistant执行addMappedStatement。往Configuration里的Map<String, MappedStatement>里面放,最后在执行接口方法的时候,从这里取


MapperStatement含有很多的属性,囊括了CRUD所能用到的

至此。才执行完了addMapper方法,也执行完了父类的InitializingBean里面的checkDaoConfig方法,由于实现了FactoryBean,需要返回被实例化的对象,因为上一步addMapper时将mapper都放到了Configuration里面,所以这里取的时候也是从Configuration里面取


同时在MapperRegistry这个类里面,看名字以为只是负责mapper注册,不要被这个名字给骗了,其实他有个getMapper方法,而且这里面也是自己维护了一个mapper的map
Map<Class, MapperProxyFactory> knownMappers = new HashMap<>();

在调用getMapper的时候,会执行代理工厂类的newInstance方法,所以其实注入spring的mapper bean是代理类,并且在调用mapper里面方法的时候会执行MapperProxy里面的拦截器方法

最终的结果是,所有的mapper类已经即将交由spring进行实例话,并且mapper类统一放在Configuration里面的MapperRegistry,每一个mapper里面的每一个方法,包括sql,返回类型,statement放在mappedStatements缓存里面,

mybatis代理拦截实现

以上如果mapper没有其他的错误,一切顺利的话项目会启动成功,至此,其他类里面的mapper就可以正常依赖,可以开始执行sql了

从这里也可以再次验证到,往spring注入的确实是代理类MapperProxy,调用mapper方法的时候,首先会执行MapperProxy的invoke,static里面的方法不要理会,只是为了兼容jdk的版本兼容

这里又引入了MapperMethod这个类,主要用于执行mapper方法的执行,包括参数解析以及返回结果的处理,算的上是框架主要执行的逻辑,从newInstace的时候这个sqlSession一直 作为参数传了进来,后续执行sql的时候也是通过sqlSession

值得主要的是SqlComment这个内部类,可以看作是MapperStatement的精简版,因为里面所有的值通过statement取出,其中type也是在构建beandefinition的时候提前解析完成的

换句话说,从调用这个mapper的方法之前,我们就已经知道了这个方法的sql类型,以及返回的结果类型,相比于select, insert、update、delete的步骤就简单很多了,显示解析参数,然后执行sql,select的话由于返回的类型居多,所以需要进一步判断,比如Mybatis plus的返回分页类型是IPage,也需要在这个时候进行判断,

这里以查询多条结果executeForMany为例,这里sqlSessioin是SqlSessionTemplate

这里首先进行参数解析,涉及到ParamNameResolver,里面是一个TreeMap用来存放变量,解析方法的参数以及注解@Param,可以看到@Param注解是可有可无,不是必须的,实际解析的时候是根据参数顺序来的,

aMethod(@Param("M") int a, @Param("N") int b) -> {{0, "M"}, {1, "N"}}
aMethod(int a, int b) -> {{0, "0"}, {1, "1"}}
aMethod(int a, RowBounds rb, int b) -> {{0, "0"}, {2, "1"}}


但是getNamedParams方法又将names做了一下操作,

参数解析完,就要准备执行sql之前的工作了,调用了sqlsession里面的方法,

像以上两种方法,效果是一模一样的
SqlSessionTemplate与SqlSession的最大的区别就是SqlSessionTemplate内部是sqlSession的代理类,并且是线程安全的,并通过sqlSessionUtils可以按需创建和销毁sqlSession,并且SqlSessionTemplate与spring的事务相集成,

这里可以看到最后的sqlSession其实是DefaultSqlSession
进入的DefaultSqlSession,会发现里面有个sql执行器Executor,并且selectOne方法是selectList方法之后get(0),并不是 limit 1来查询


从Configuration获取到MappedStatement就开始调用执行器里面的方法
这个执行器又是从哪来的呢,其实是刚才SaleSessionTemplate代理方法里面新建DefaultSession的时候根据executorType创建的,也就是在配置文件当中可以指定的,默认值为SIMPLE

如果cacheEnabled为true,实际上默认值也是true,会创建一个CachingExecutor执行器

最终执行的query方法

如果是特殊类型的话会在Configuration中有一个resultMapId,这个时候sql语句,以及stmt需要的参数都已经准备好了

下一步就是判断是否查询缓存,这里我们没有开启缓存,直接往下走,这里的delegate是SimpleExecutor,属于套娃类型的装饰器模式


其中queryFromDatabase的doQuery才是真正开始查询的地方,但是还没有到jdbc,其他的地方都是在维护缓存,紧接着,调用configutaion创建了RoutingStatementHandler,StatementHandler主要的作用是处理Statement
RoutingStatementHadnler相当于一个策略模式,根据statement返回对应的Handler,这里执行的sql语句为 select id, name, state, country from city where state = ?,
所以采用PREPARED,

接下来大致的流程就是属于jdbc的操作了,创建连接,创建Statement,以及statement.execute,结果处理,关闭statement,关闭conn


至此,Mybatis注入以及执行sql的逻辑大致就是这样,当然还有好多细节没时间看的,有时间再慢慢研究

Mybatis的Cache
Mybatis与Spring事务结合
Mybatis, xml文件以及LanguageDriver
Mybatis的插件机制

标签:mapper,解析,里面,窥探,源码,sql,Mybatis,执行,方法
From: https://www.cnblogs.com/LiuFqiang/p/18254987

相关文章

  • 可预约上门服务的在线DIY预约小程序源码系统 带完整的安装代码包以及搭建教程
    系统概述这款可预约上门服务的在线DIY预约小程序源码系统是为满足各类上门服务需求而设计的。它允许用户通过小程序方便地预约各种服务,如家政服务、维修服务、美容美发服务等。同时,商家可以在后台管理系统中方便地管理预约信息、服务项目、员工信息等。代码示例系统特色......
  • 万能DIY预约小程序源码系统 适合任何行业在线预约报名 带完整的安装代码包以及搭建教
    系统概述在当今数字化时代,线上预约和报名系统已经成为各行各业不可或缺的工具。为了满足市场需求,万能DIY预约小程序源码系统应运而生,它为各类企业和组织提供了一种便捷、高效、灵活的解决方案,可适用于任何行业的在线预约和报名需求。代码示例系统特色功能一览  预约......
  • 【ASeeker】Android 源码捞针,服务接口扫描神器
    ASeeker是一个Android源码应用系统服务接口扫描工具。项目已开源:☞Github☜如果您也喜欢ASeeker,别忘了给我们点个星。说明ASeeker项目是我们在做虚拟化分身产品(『空壳』)过程中的内部开发工具,目的是为了提升Android系统各版本适配效率。由于产品需支持Android9......
  • Java计算机毕业设计+Vue实习实训管理系统(开题报告+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今社会,实习实训已成为高等教育中不可或缺的一部分,对于学生实践能力和职业素养的提升具有重要意义。然而,传统的实习实训管理方式存在着诸多不便,如......
  • 前端网站(一) - 登录页面及账号密码验证(完善版)【附源码】
    登录页面及账号密码验证开篇(请大家看完):此网站写给挚爱,后续页面还会慢慢更新,大家敬请期待~~~轻舟所编写这个前端框架的设计初衷,纯粹是为了哄对象开心。除此之外,并无其它任何用途或目的。此前端框架,主要侧重于前端页面的视觉效果和交互体验。通过运用各种前端技术和创意,......
  • 广告变现小游戏对接广告平台开发 源码搭建
    对接广告平台以实现小游戏广告变现,并搭建相应的源码,是一个包含多个技术环节的过程。这涉及到游戏开发、广告SDK集成、后端服务配置等多个方面。以下是一个大致的开发与搭建流程:一、游戏开发需求分析:首先明确小游戏的定位、玩法、特色以及目标用户群体,为后续的广告变现策略奠......
  • 基于java的酒店管理系统(ppt+ER图+流程图+需求背景数据库+架构说明有文视频源码齐全)
    1引言1.1编写目的本文档为阳光酒店管理系统概要设计说明书,为阳光酒店管理系统详细设计的主要依据。1.2背景本软件全称为XX酒店管理系统。本软件为清华IT学习认证中心1123班顾俊小组毕业设计项目,由小组成员顾俊、董丰共同完成。软件适用于普通二星级酒店、宾馆。1.3参考资......
  • mybatisplus代码生成
    1.引入依赖点击查看代码<!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.......
  • 基于springboot实现课程答疑管理系统项目【项目源码+论文说明】计算机毕业设计
    摘要随着信息互联网信息的飞速发展,无纸化作业变成了一种趋势,针对这个问题开发一个专门适应师生交流形式的网站。本文介绍了课程答疑系统的开发全过程。通过分析企业对于课程答疑系统的需求,创建了一个计算机管理课程答疑系统的方案。文章介绍了课程答疑系统的系统分析部分......
  • 基于springboot实现宠物咖啡馆平台管理系统项目【项目源码+论文说明】计算机毕业设计
    摘要随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了基于SpringBoot的宠物咖啡馆平台的设计与实现的开发全过程。通过分析基于SpringBoot的宠物咖啡馆平台的设计与实现管理的不足,创建了一个计算机管理基于SpringBoot的......