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

news/2024/9/28 13:28:22

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的插件机制

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

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

相关文章

5.21实验三 Web数据库程序设计

一、实验目的 通过使用JSP技术设计一个简单的数据库管理系统,了解展示页面和编辑页面的区别,掌握Web服务器与MySQL数据库的连接和数据库操作的方法,掌握使用Java语言编写JSP文件的方法。 二、实验内容和基本要求 从以下列举的四个数据库中,任选其一,或者自行定义其他数据库…

应对高温“烤”验,数据中心降温奇招来了!

近日,中国电子学会联合中国电子技术标准化研究院等单位,在安徽省合肥市举办“节能服务进企业”暨绿色数据中心对接推广活动。会议为天翼云存储资源盘活系统HBlock入选《国家工业和信息化领域节能降碳技术装备推荐目录(2024年版)》颁发荣誉证书;天翼云存储产品专家肖夏敏发…

热҈热҈热҈!天翼云开出解暑“凉方”!

天翼云依托技术、产品优势,为建筑工地提供覆盖采集、传输、处理、分析、展现等环节的视频系统整体构建方案,通过搭建智慧工地智能监控平台,实现对人、机、料、法、环全方位实时监控,为施工建设全周期提供安全保障。通过专网连接,实时监测工地温度、湿度及气候变化趋势,及…

数据库数据恢复-oracle数据库常见故障及数据恢复分析

作为存储和处理数据的系统,oracle数据库在使用过程中不可避免会出现各种导致数据丢失和数据损坏的故障。总结出oracle数据库常见故障以及恢复可能性。1、Oracle数据库无法启动或者启动后无法正常工作。 如果故障是突发性的,通常情况下恢复的可能性极高。出现这类故障后,首先…

服务器数据恢复-重建MDisk导致VDisk丢失的数据恢复案例

服务器数据恢复环境: IBM某型号存储; Solaris操作系统,部署Oracle数据库。服务器故障: 重建MDisk导致对应的存储池中的VDisk丢失,导致Solaris操作系统中的Oracle数据库无法使用。 服务器数据恢复过程: 1、将所有涉及到Oracle数据库的VDisk以只读模式连接到备份服务器上,…

服务器数据库

数据库往往是服务器中最核心的部分,所以一旦数据库发生损坏,将会带来巨大的损失,因此数据库的数据恢复功能变得越来越重要了。 在服务器运行过程中,由于断电、操作不当原因损坏到服务器的硬盘的时候,怎样才能恢复网站服务器的数据呢?一、服务器存储系统非常重要,硬盘作为…

遭到勒索攻击,只因打开了陌生邮件

有网络安全意识的大都知道,陌生的邮件最好不要打开,更不要去打开附件,这很有可能是黑客传播的邮件。而近日,就有随手点开了这样一封电子邮件,而让办公室内的三台服务器都相继感染了360勒索,幸运的是这三台服务器中都没有保存什么太过重要的数据,不需要花费大量的时间重建…

单细胞测序最好的教程(十六):关于RNA速率你想知道的都在这

作者按 本章节详细讲解了基于RNA速率的三种拟时序模型,包括稳态模型,EM模型和深度学习模型,并对比了不同模型的适用场景与计算特点。本教程首发于单细胞最好的中文教程,未经授权许可,禁止转载。 全文字数|预计阅读时间: 5000|10min ——Starlitnightly(星夜)5.2 RNA速率…