从源码的角度探究 mybatis 是如何整合到spring中的。

1. 前言

1.0 前言中的前言

在看此文章的时候,建议要对 spring 的初始化的整个大概的流程 要有所了解,为什么呢?

因为对 spring 都没有这样一个基本的认知的话,那"整合到 spring "中相关的知识就更别提了。

通过了解mybatis 是如何整合到spring 的相关知识之后,我们就可以在以后 有自己的框架之后(或者类似的情形)整合到spring 中了。

1.1 什么叫整合到spring?

把某框架整合到spring,天天这么说,那么什么叫整合到spring 呢?

那么我分享一下我自己的见解:

Foremost amongst these is the Spring Framework’s Inversion of Control (IoC) container.

上面的那句话来自spring 官方文档,所谓 “整合到spring” 就是把某个框架中用到的 bean 放入 spring的 IoC container 当中让 spring 来帮助我们管理。

1.2 为什么要整合到spring?

那为什么要整合到spring? 这个问题就跟你为什么要使用spring 的原因是一样的。这个原因我就不需要再阐述了。

2. 前置环境的准备

为了方便后面的解释,我这里就给出了例子的环境。

2.1 pom 坐标

不用多说。我这里就不给出了。显得乱。

2.2 spring 的配置文件

经过下面的基本配置,就可以把 mybatis 整合到 spring 中了。其中图中标注的地方为关于mybatis 的配置。

在这里插入图片描述

2.3 mapper接口和其配置。

这里有两个mapper,一个是UserMapper, 另一个是OrderMapper,还有就是他们两个mapper对应的xml映射文件。

在这里插入图片描述

2.4 Service 类

UserService 调用 UserMapper,我们平常一般也是这样用把。

在这里插入图片描述

2.5 测试方法的编写

@Test
public void test() throws Exception {
    
    

    // 加载 创建 spring 环境
    ClassPathXmlApplicationContext context =
        new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

    // 从 spring ioc 容器中取出 UserService 类型的bean
    UserService userService = context.getBean(UserService.class);

    // 执行其内的方法。并打印结果
    userService.findAll().forEach(System.out::println);

}

3. 从中发现的问题

从第2 节前置环境的准备。我们知道了三处关于mybatis 的配置与问题。那么关于mybatis 是如何整合到spring中的知识,一定会在这 三处 中进行体现。

3.1 SqlSessionFactoryBean 的配置

此处说明了一个问题,我们把 SqlSessionFactoryBean 放入了Spring 的容器当中,那么这个类的作用是什么呢?

这个类从名称也能看出来,是用来获取SqlSessionFactory对象的。

细节的问题会从下面阐述
在这里插入图片描述

3.2 MapperScannerConfigurer 的配置

也是往spring的容器中放入了一个 MapperScannerConfigurer 类型的对象。那么这个是用来干什么的?


这个从配置中类名字也能看出来,是用来扫描我们的mapper 接口文件的。扫描完成了之后,他就会将其生成对应的BeanDefinition, 之后将其放入 spring 中对应的位置(用来保存beanDefinition的变量,这里不详细说明)。

放入之后,到spring 初始化bean的过程中,就会从 保存BeanDefinition 的变量 中获取对应的BeanDefinition ,之后根据BeanDefinition 生成对应的bean 保存到对应的位置(此位置可以理解为狭义的ioc 容器)。


那么具体的详细信息在后面会阐述。

在这里插入图片描述

3.3 mapper的注入体现出的问题

那么从这张图里面可以证明什么样的问题呢?很明显,这里的 userMapper 显然是spring 帮我们注入进来的,也就是说,此mapper已经放入了spring 容器中。

那么换句话说是MapperScannerConfigurer (上面的 2) 的功劳。那么这个猜测是否正确呢?下面查看源码即可证明。

在这里插入图片描述

4. 源码探究

根据第3节中提出的三处内容,将在本节来进行相对详细的阐述。

4.1 配置SqlSessionFactoryBean 的作用

我们把这个类交给了spring 管理,所以就跟进源码来看一看这个类的作用。


在这里插入图片描述

这个类,上来就实现了 FactoryBeanInitializingBean 两个接口,这两个接口是干什么的相信大伙都知道吧。

首先实现FactoryBean 那证明他是一个 FactoryBean。那么一般情况下,在spring 中获取的对象 都是 其getObject() 方法返回的对象。 通过这个方法,我们就可以知道他创建了一个什么对象返回。

而又实现了InitializingBean 接口,那就证明了:在这个 bean 在初始化的时候,放入spring 的 容器之前呢,肯定是调用了bean生命周期中的afterPropertiesSet() 方法做了一些事。这个方法的代码是我们需要查看的。

4.1.1 getObject()

从这个代码可以看出来 这个方法 返回了一个 SqlSessionFactory 对象, 所以这个类 是用来 创建/获取SqlSessionFactory 对象的。

这个方法基本没干啥,主要是调用了afterPropertiesSet() , 所以主要的逻辑是在此方法中。

那感情好啊,需要看两个方法,现在直接成了一个,爽了。。。

在这里插入图片描述


4.1.2 afterPropertiesSet()

又调用了 buildSqlSessionFactory() 方法,所以需要进入。

在这里插入图片描述


下面是 buildSqlSessionFactory() 方法的一开始,其中Configuraiton 对象是啥东西?Configuration 对象中保存了mybatis 的所有的配置。

详情请看我的这篇文章。点击跳转

下面的if 判断对应了三种整合的配置方式。

  • 第一种

    图中的第一行的框 对应第一种配置方式,也就是Configuration 对象不是 解析 mybatis 主配置文件来的,而是我们自己主动设置的(使用 标签注入到spring中)。这样配置的好处就是可以实现 无mybatis 配置文件整合spring

  • 第二种

    也就是有mybatis 主配置文件的整合方式,这个就不用说了,这个Configuraion 对象的由来,就是解析 mybatis 主配置文件获得的。

  • 第三种

    这第三种,就是第一种情况,就是将第一种中的 Configuration bean的配置去掉

    其实也不算第三种,也就是 我们没有提供 mybatis 的主配置文件。所以无法解析获得Configuration

    我们也没在spring 中配置Configuration 对象,所以也无法获得。那没办法,他就自己new 了一个。

    那么它new 的是一个没有各种属性的Configuration 啊,里面没有什么配置保存的。

    其实不然,我们在配置 SqlSessionFactoryBean 的时候也可以设置一些值的,这些值,都保存到了Configuration 对象中了。如图 第三行的红框了。里面的 可以配置好多。设置其属性。譬如我们就设置了<property name=“typeAliasesPackage” …/> 给我们的pojo 设置了一个别名。

    也就是说啊。这样配置按理说,能配置的功能应该不多,只能配置 SqlSessionFactoryBean 中提供的。。。

在这个方法的下面,会将给 SqlSessionFactoryBean 设置的值 给设置到 Configuration 对象中。请看下面的第二张图

在这里插入图片描述

图二:

在这里插入图片描述

4.1.3 配置SqlSessionFactoryBean作用总结

题外话: 其实这里也验证了 我的一个文章的猜测:(关于"mybatis 整合spring" 不带mybatis 主配置文件的问题)。 文章链接

SqlSessionFactoryBean 是一个 FactoryBean ,它的作用是生产SqlSessionFactory。

那么在其生命周期的 afterPropertiesSet() 方法中,对 SqlSessionFactory 做了初始化,比如Configuration 对象的生成。


那么从中我们可以学到什么呢?

可以看到SqlSessionFactory 对象是mybatis 中的一个非常重要的对象,没有它也就啥都没有了。

那么如果我们也有一个框架,这个框架中的重要的对象, 也可以使用类似的方式将其交给spring 进行管理。

因为 SqlSessionFactory 对象的创建比较复杂,所以它使用了FactoryBean的方式

4.2 配置MapperScannerConfigurer 的作用

我们把这个类交给了spring 管理,所以我们就应该分析这个类的作用。

在这里插入图片描述

看到这个图片中标注的这个BeanDefinitionRegistryPostProcessor接口,我就获得了大量的信息。如果我们实现了这个接口,我们就可以定义自己的BeanDefinition 然后注册到spring 中, 它和它的父接口都是 spring 为我们们提供的非常重要的扩展点。所以那些mapper放入spring 容器的功能就肯定是这个类完成的了。那主要的业务逻辑肯定是的postProcessBeanDefinitionRegistry()方法中了,此方法是 BeanDefinitionRegistryPostProcessor 接口中的方法。


在这里插入图片描述

创建了一个扫描器,用来扫描class,然后将其对应的beanDefinition 注册到spring中。

下面进入scan()方法


在这里插入图片描述

调用的scan() 方法是spring中的方法了,这边解释一下:

ClassPathMapperScanner 是mybatis 提供的扫描包的类,它继承了spring的 ClassPathBeanDefinitionScanner ,因为ClassPathMapperScanner 并没有scan() 方法,所以在此处调用的是其父类ClassPathBeanDefinitionScanner 的scan方法。

​ 在此方法中。doScan() 方法调用的是mybatis的ClassPathMapperScanner中的doScan(); 原因是ClassPathMapperScanner 重写了 ClassPathBeanDefinitionScanner的doScan() 方法, 所以下面我们来到ClassPathMapperScanner 中的 doScan() 方法。


在这里插入图片描述

这里看注释就行了。下面就进入 processBeanDefinitions 方法,来看看spring 做了那些特殊的处理?


在这里插入图片描述

看图片中的注释即可。

至此,mybatis 已经把 mapper 对应的 beanDefinition 注入到了spring中,那么接下来的事情就是spring 做的了。

spring 根据bd 把他们实例化,该依赖的对象就依赖。

4.3 其他的一些细节

通过上面的分析知道,放入spring容器的并不是mapper接口,而是生产其mapper代理对象的 工厂类(MapperFactoryBean)这是个FactoryBean 没什么好说的。

下面我们来看一下这个类中的细节。

  • 细节1

    直接看图
    在这里插入图片描述

  • 细节2

    看过mybatis 源码的都知道。其mapper 代理对象执行需要SqlSession 对象。那我们再配置的时候没有指定。如图

    在这里插入图片描述

    所以它再生成对象的bd 中也就没有设置。如图

    在这里插入图片描述

    但是吧,生成mapper代理对象的时候,还特么需要这个(看过mybatis 源码的应该都知道)。如图:

    在这里插入图片描述

    那么这个SqlSession spring 是如何给他注入进来的呀?经过我的调试,我发现了 一开始bd中的确是没有

    但是再经过spring 的populateBean() 的方法中的autowireByType(beanName, mbd, bw, newPvs);

    它就有了, 我恍然大悟,他肯定是设置了 byType 注入啊。那么spring 会去容器中找啊调用其set方法,给其注入啊。那么就去看看,mybatis 再设置bd 的时候,是否设置了byType 的注入方式?那么代码一查看 的确有::如图::

    在这里插入图片描述

    那么问题又来了,在我们的spring 的容器中。我们没有吧SqlSession 扔进容器过呀?那么就该看看这个getSqlSession()方法了。 来到此方法一探究竟:如图

    在这里插入图片描述

5. 总结

那mybatis 是如何整合spring的?

那么简单点来说,就是运用了spring 中的扩展点--------------BeanDefinitionRegistryPostProcessor 将mapper 扫描放入spring 容器中。其中因为获得的mapper并不是接口,是其代理类, 流程比较繁琐, 所以mybatis 中运用了大量的FactoryBean来获取对象。


以上的都是我自己的见解。希望不要误人子弟。

原创,谢谢

猜你喜欢

转载自blog.csdn.net/weixin_42041788/article/details/108037005
今日推荐