文章目录
前言
在上一篇中,我们对MyBatis的整体项目结构进行了大览,本篇,开始正式进入源码分析的第一篇,来一起了解一下MyBatis的mapper interface
与XML配置文件的加载过程。
本篇源代码基于SpringBoot 1.5x
,Mybatis-spring 1.3x
、Mybatis 3.4x
。
SpringBoot加载MyBaits的过程
在Spring项目中,我们对Mybatis进行配置时,往往只需要添加几个注解,就可以将mapper interface
托管给Spring,就像这样:
那么这里就有两个问题:
1、Spring是如何扫描到Mapper interface
的,并加载到Spring容器中去的呢?
2、mapper interface
是如何与XML配置文件建立起关联的呢?
下面,我们来分析一下Mybatis的mapper
加载过程。
Mapper加载过程
Mybatis在SpringBoot的加载过程,依托于SpringBoot的autoconfig机制,如果您对SpringBoot有过了解,一定知道SpringBoot中最核心的部分,就是autoconfig机制,也是SpringBoot集成其他框架的主要方式,这里就不对SpringBoot的autoconfig机制进行过多的介绍,毕竟本篇的主角是Mybatis,感兴趣的看官可以自行查找相关资料。
如果是通过@MapperScan
的加载方式,则会使用MapperScannerRegistrar
为扫描入口,而如果通过XML配置文件指定mapper
的方式,则会通过MybatisAutoConfiguration
作为扫描入口。
本篇,我们主要介绍使用@MapperScan
的加载方式,同时笔者也推荐使用此种方式,可以更加符合SpringBoot的面向注解编程的思想。
想了解Mapper的加载过程,我们需要找到一个切入点,而这个切入点,就是SpringBoot启动类上的注解,@MapperScan
。
MapperScan
我们进入MapperScan
中,发现注释上关联了两个类,一个是MapperScannerRegistrar
,一个是MapperFactoryBean
,通过命名就可以看出,毫无疑问,MapperScannerRegistrar
就是Mapper
的扫描器,我们进入MapperScannerRegistrar
。
MapperScannerRegistrar
MapperScannerRegistrar
实现了ImportBeanDefinitionRegistrar
接口,并实现了registerBeanDefinitions
方法,因此在Spring启动时,会调用所有实现了ImportBeanDefinitionRegistrar
接口的类的registerBeanDefinitions
方法,我们来具体分析一下registerBeanDefinitions
中的实现:
上面就是registerBeanDefinitions
的实现,核心主要分为5个步骤:
1、解析@MapperScan
注解中的属性内容,并以K-V
的形式存储进实现了LinkedHashMap
的AnnotationAttributes
对象中;
2、创建ClassPathMapperScanner
对象,该对象是扫描mapper interface
的核心;
3、组装ClassPathMapperScanner
对象中的各个属性字段值,其中核心的属性字段包括factoryBean
、sqlSessionFactoryRef
、basePackages
;
4、开始加载指定包路径下的类对象。
特别注意:
需要注意的一点是,对于通过
@MapperScan
的扫描方式,默认会加载指定包路径下的全部类对象进入Spring容器中,但是对于MybatisAutoConfiguration
的扫描方式,则需要在mapper interface
上加入@Mapper
注解才可以被Spring扫描。原因是因为
ClassPathMapperScanner
的registerFilters
方法,其实现如下:
这里我们不过多的探究其具体实现细节,而是从注释中就可以得到核心信息,当指定了
annotationClass
与markerInterface
属性后,ClassPathMapperScanner
只会扫描符合条件的接口实现类,而MybatisAutoConfiguration
的方式指定了annotationClass
为@Mapper
。
ClassPathMapperScanner
我们继续来看ClassPathMapperScanner
中的doScan
方法的具体实现:
doScan
中主要做了两件事:
1、扫描指定包路径下的类,并将其包装为BeanDefinitionHolder
,组装成一个集合;
2、处理BeanDefinitionHolder
集合中的全部对象的属性字段值。
我们来看一下processBeanDefinitions
的实现细节:
上面就是processBeanDefinitions
的实现,其中主要是处理BeanDefinitionHolder
的属性字段,而其中最核心的操作是设置definition
对象的BeanClass
字段,将其设置为MapperFactoryBean class
:
definition.setBeanClass(this.mapperFactoryBean.getClass());
通过MapperFactoryBean
,可以将mapper interface
注入进BeanFactory
中,我们接下来看一下MapperFactoryBean
的实现。
MapperFactoryBean
MapperFactoryBean
从名字中我们就可以看出来,这是一个Mapper bean
的工厂类,它继承了SqlSessionDaoSupport
类并实现了FactoryBean
接口,具备了FactoryBean
的能力。
我们来看一下其核心实现方法:
而在初始化的过程中,由于SqlSessionDaoSupport
实现了InitializingBean
接口,并在初始化方法中调用了子类实现的checkDaoConfig
方法,而checkDaoConfig
方法是非常核心的所在,通过该步骤,才可以将mapper interface
与mapper xml
进行关联组装,让我们来深入探究一下。
在checkDaoConfig
方法中,主要做了两件事情:
1、检查mapper interface
是否已经完成过初始化;
2、如果没有,进行装载操作。
我们进入configuration.addMapper(this.mapperInterface)
方法一探究竟:
这里调用了mybaits中的Configuration
的addMapper
方法,Configuration
类是一个非常核心的类,后面的章节中我们会着重介绍,这里暂时忽略,继续跟进:
进入MapperRegistry
的addMapper
实现,其中主要进行了两件事情:
1、将mapper interface
作为key
放入Map
集合中,value
设置为MapperProxyFactory
;
2、将mapper interface
继续解析处理。
这里将mapper interface
至关重要,我们先分析第二步,然后在回头来说这一步。进入parser.parse()
方法:
在parse
方法中,进行大量的解析工作,我们需要关注的是红框部分,这里Mybaits会进行两部分判断:
第一步:尝试解析mapper interface
对应的XML配置文件;
第二步:尝试解析mapper interface
上的注解字段。
我们先忽略具体的解析过程,这一部分的分析将放到下一篇中仔细讲解,parseStatement
方法非常的冗长,主要是进行各种解析的工作,我们核心关注的点:
我们进入该方法内部,马上快要到底真相的所在:
看到这里,你是不是突然发现了这熟悉的配方,这熟悉的味道?MyBatis将mapper
XML文件中的标签或mapper interface
上的注解进行解析,最终组装成MappedStatement
对象,而MappedStatement
则是Mybatis中最为核心的类之一,通过它,才可以真正的执行SQL语句。
Mapper加载全流程
最后,我们用一张简易的流程图来概括一下mapper
加载的过程:
结语
本篇我们介绍了SpringBoot对Mybaits Mapper的核心加载过程,对Mybatis中的一些核心类有了初步的认识,同时也解释的文章开头的第一个问题:
1、Spring是如何扫描到mapper interface的,并加载到Spring容器中去的呢?
但是第二个问题:
2、mapper interface是如何与XML配置文件建立起关联的呢?
那么在下一篇中,我们就来解答第二个问题,深入探究Mybatis是如何解析XML配置文件与接口注解的,而又是如何与mapper interface
建立关系的,敬请期待!
更多精彩文章, 请关注我的个人公众号:老宣说
让我们一起共同学习成长!
感谢您的支持!