最近一直处于疯狂读源码的状态,简直就是少壮不努力,老大徒伤悲。言归正传,虽然所在的公司用的框架都是内部的,基于开源的框架封装的,但是平时自己写东西的时候,还是喜欢直接用开源的框架。在大学的时候就用过SpringBoot,工作以后也用过Mybatis,但是会用却不是很了解原理。最近就很好奇,SpringBoot的项目通过starter的形式集成Mybatis后,启动的流程与原理,于是就好好的梳理了一下。
看之前,首先需要了解下SpringBoot的启动流程,推荐下面的原创博客,看了以后,对SpringBoot的启动流程了解了大概,不然笔者也是云里雾里的:
https://www.cnblogs.com/hello-shf/p/10976646.html
看完以后,开始介绍SpringBoot 集成 Mybatis 启动流程与原理
首先,先介绍下笔者的示例项目的信息:
- springBoot版本:2.2.0.RELEASE
- mybatis版本:3.5.2
通过starter引入
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
项目中,只有一个Mapper接口,如下,只有一个查询接口。
启动类上,没有关于Mybatis的注解,也没有自定义的Mybatis的配置类,配置文件中也没有特殊的配置。接下来开始描述流程:
另外说明:最近想把项目用开源的框架再撸一遍,因此一些内容可能是公司项目的命名,因此都马赛克了,以后有空换个简单的项目把截图再替换下吧。
1. Mybatis starter 通过SPI扩展机制实现的自动装配
找到对应的Spring.factories
spring.factories的配置如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
具体看MybatisAutoConfiguration这个配置类
2. 实例化sqlSessionFactory和sqlSessionTemplate
其实大家都知道 对应Mapper的接口,实现原理其实是代理,但是具体流程是怎么实现的呢
3.在Springboot启动,刷新应用上下文时,即执行SpringApplication的refreshContext方法
请锁定PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法中的这段代码
查看变量postProcessorNames的内容
存在MapperScannerConfigurer
查看MapperScannerConfigurer类,一开始很疑惑,因为在这个类上并没有@Component注解。那是怎么注册到BeanDefinition中的呢,后来发现了如下代码,将MapperScannerConfigurer注册到BeanDefinition中:
言归正传,来看看如何实现扫描的
4.注解扫描
因为MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,因此在refresh时,会调用postProcessBeanDefinitionRegistry方法。
一步步往里看,到ClassPathBeanDefinitionScanner类中doScan方法,findCandidateComponents是去扫描指定的包中@Mapper的接口。
从这里可以看出,已经搜索到@Mapper注解的接口了。
但是看这个definitionHolder,总觉得有点奇怪
ClassPathMapperScanner类中,扫描完,beanDefinitions中存放着扫描到的所有的mapper的接口,然后执行processBeanDefinitions方法。
在processBeanDefinitions方法中,会对刚刚的beanDefinition进行进一步加工,这也解决了我之前的疑惑。
再看看beandefinition
5.如何实例化代理类
由于实例化容器是一大堆关于spring的逻辑,因此粗粗略过。
看DefaultListableBeanFactory的resolveDependency方法,顾名思义,应该是解决依赖的方法,因为serviceImpl类中,成员变量xxxDao是以autowrie的形式注入进来的,因此需要实例化xxxDao进行注入。
跟着代码一步步追进去,就能发现AbstractBeanFactory的doGetBean中,以下方法,getSingleton,会先判断该单例的bean是否已经被创建,如果没有,则创建
最终会调用MapperFacctoryBean的getObject方法中,获取代理类
这样在使用的时候,获取的便是代理类。
另外,当多个xxxServiceImpl依赖同一个@Mapper注解的接口时,如果该接口已经实例化过后,便可以从以下方法中获取之前实例化的bean,且代理类是同一个对象。
在第一次创建时,便会将实例的对象放在factoryBeanObjectCache中。
最后
追了上面的代码以后,也算大致了解许多SpringBoot的Starter是怎么集成的,融会贯通。
另外之前读过一些Spring的书籍,也算用了很久的Spring框架了,但是一直没有深究过源码,偶尔看看别人的博客,但是这次感觉纸上得来终觉浅,还是得追着代码看下,感觉对Spring框架还是很不熟悉,而且读源码的确是很枯燥,最近有点心血来潮,但还是希望以后能一点一滴,慢慢积累。