简单写一篇文章分析下Myabtis是如何整合到Spring容器中的以及我们编写的Mapper又是如何运行的。

从@MapperScan注解开始

使用MapperScan注解指定mapper的位置。

我们看下MapperScan注解的作用。

从上面我们可以看到一个关键信息,使用@Import导入了MapperScannerRegistrar这个类。

MapperScaneerRegistrar是ImportBeanDefinitionRegistrar的子类。

使用@Import修饰ImportBeanDefinitionRegistrar的子类,会被Spring在启动中载入到容器,并会在初始化阶段调用registerBeanDefinitions方法,这个对于第三方框架来说是一个不错的扩展点。

MyBatis可以将自定义的BeanDefinition导入到Spring 管理。这个自定义的BeanDefinition就是开发者编写的Mapper。

我们看看这个MapperScannerRegistrar具体做了些什么工作。

MapperScannerRegistrar

首先实现下registerBeanDefinitions,用于监听Spring容器初始化。

之后调用到registerBeanDefinitions

这个过程结束后,容器中会多了一个MapperScannerConfigurer的BeanDefinition。

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,在注册完BeanDefinition后会自动回调postProcessBeanDefinitionRegistry

我们看下注册后的实现。

看到代码就可以猜到,扫描basePackage的工作是由ClassPathMapperScanner类进行完成的。

在doScan方法中,先调用super获取到包下面所有扫描到的BeanDefinitionHolder。

在Mybatis的自定义实现中,会将BeanDefinition的class设置为MapperBeanFactory,然后注册到容器中。

我们看到,所有扫描到的BeanDefinition的BeanClass都被设置成了MapperFacatoryBean,这一步至关重要,当初始化bean时,会调用到BeanFactory的getObject方法来初始化对象。

MapperFactoryBean

我们看下MapperFactoryBean的getObject方法

这里getSqlSession返回的是SqlSessionTemplate实例,SqlSessionTemplate也是一个单例,由spring-boot-starter-mybatis进行配置。

MapperFactoryBean在构造完成后会调用checkDaoConfig将MapperClass设置进MyBatis的Configuration,这样之后就可以调动getMapper获取mapper使用了。

整合spring事务

下面我们介绍下Mybatis是如何将事务整合仅Spring的事务体系的。

我们直接使用的Mapper是MyBatis通过Configuration创建的动态代理MapperProxy。

MapperProxy中出来CRUD的关键代码是cacheInvoker

Mapper的这些方法最终还是会转移到SqlSession中进行执行,而Mybatis在Spring中的SqlSession是SqlSessionTemplate。我们只需要分析SqlSessionTemplate的CRUD方法即可看出对Spring的相关整合。

我们看下SqlSessionTemplate是如何构造的

其中最关键的元素是sqlSessionProxy,Mapper相关的操作是由sqlSessionProxy间接完成的。

sqlSessionProxy

我们可以看出,SqlSessionProxy其实是一个动态代理,真正的实现是SqlSessionInterceptor。

相当于每次调用sqlSession的CRUD方法,都是直接调用到这里面来的。

这一部分逻辑我们梳理下,分为几个步骤:

  1. getSqlSession获取sqlSession,这一部分又分为是否在事务中两处逻辑
    1. 在事务中, 直接返回事务中的sqlSession
    2. 不在事务中,调用opepSession重新获取一个
  2. 使用这个获取到的sqlSession执行对应的CRUD方法
  3. 在事务中的话,执行完调用commit
  4. 关闭sqlSession

Mybatis与Spring的整合大概就是这样,后面会分析Mybatis中的其他设计。

总结

最后总结下上面的过程。

我们通过@MapperScan注解导入MyBatis的ClassPathMapperScanner。Scanner扫描指定basePackage路径下的所有BeanDefinition,并将BeanDefinition的class设置为MapperFactoryBean。

MapperFactory是构造Mapper的工厂,构造Mapper会通过SqlSessionTemplate进行。

SqlSessionTemplate将获取session的过程与spring的事务整合,使一个事务内的操作均由一个SqlSession完成。

Mybatis与Spring的整合大概就是这样,后面会分析Mybatis中的其他设计。

发表评论

邮箱地址不会被公开。 必填项已用*标注