Spring 如何执行 mybatis 中的 mapper 接口并执行sql

198 阅读4分钟

多言数穷,不如守中。

1 前言

Mybatis 在日常的开发中是十分常用的 orm 框架,开发者通常在 mapper 包下定义接口,然后在 xml 文件中定义接口实现的 sql 语句,我们都知道 mapper 是一个接口,在 spring 容器中是如何管理这些mapper 接口并执行的呢?本文将结合实践操作来揭示其作用原理和运行机制。

2 spring 的扩展点

Spring 在创建一个 Bean 的时候,会通过注解或者配置的方式读取,并将其注册成一个BeanDefination。但是 spring 注册时会排除抽象类或者接口, 所以对于 Mapper 接口的注入面临的第一个问题就是如何注入到 spring 容器中。

  • 扩展点 ClassPathBeanDefinitionScanner#isCandidateComponent

通常情况下 Spring 通过 BeanDefinitionRegistryPostProcessor 来实现 Bean 的注册, 通常在 spring 容器初始化时实现这个接口,通过实现其 postProcessBeanDefinitionRegistry 方法来扫描需要注册的 Bean。 但是在 mybatis 中, mapper 的扫描和注册实现方式有所不同,其实现通过 ClassPathMapperScanner来处理, 它继承了 ClassPathBeanDefinitionScanner (其父类ClassPathScanningCandidateComponentProvider,其中有 isCandidateComponent 方法) 类,重写了其父类中的 isCandidateComponent 方法,使得 Mapper 接口的注入得以实现,如下图所示:

1722699363763.png

  • 扩展点 FactoryBean#getObject

首先我们需要了解一下 BeanFactoryFactoryBean 的区别,可以看之前的文章介绍。在 mybatisSqlSessionFactoryBean 中,其通过实现 FactoryBean 接口,在 getObject 方法中创建 jdk 动态代理,其创建的对象是 SqlSessionFactory ,通过 SqlSessionFactory 可以获取 SqlSession, 即获取到了一个操作数据库的连接。具体如下图所示:

1722700954714.png

如下图所示,即 SqlSessionFactoryBeanSqlSessionFactoryBuilder

1722701275650.png

3 mybatis 如何获取配置

如前文所示,配置文件的加载可以通过 SqlSessionFactoryBuilder.build 来实现配置的解析和加载。配置文件默认是 xml 格式,通过 XMLConfigBuilder.parseConfiguration 方法可以实现配置的解析,通常配置文件包括数据库的连接,实体类的配置,环境信息以及 mapper 的配置。

1722764056474.png

SqlSessionFactory 本身是一个接口,DefaultSqlSessionFactory 则是实现了 SqlSessionFactory 的实现类,保存好 configuration 之后返回,就得到了我们开头需要的 SqlSessionFactory 实例。

解析配置文件如下图所示,parse 方法返回一个 Configuration 对象。其中parsed 是一个布尔类型成员变量,默认值是 false,其目的是为了防止多线程情况下该方法被二次调用。 1722764777019.png

4 mybatis 中获取 mapper

mapperElement 方法用来解析 mappers 文件,通常 mapper 有四种方式来配置,即 package、resources、url、class。在 MapperRegistry 中的 addMapper 方法中,通过 MapperAnnotationBuilder.parse 用来解析 mapper 文件中的方法、注解等信息。 1722765034499.png

综上,mybatis 添加 mapper 的流程如下图所示:

v6b2v8u237hl.png

5 mybatis 执行sql流程

之前已经介绍了 mapper 的获取过程,那么具体的 sql 要怎么去执行呢? 经过之前的流程,我们已经获取到了 SqlSessionFactory,我们可以通过 openSession 方法从工厂中获得 SqlSession 的实例对象。如下图所示,mybatis 一共有四种执行器,分别是 simple、reuse、batch, 默认使用的是 simple 执行器,如果使用了二级缓存,则会创建 CacheExecuter 执行器。

1722767185387.png

通过以上的操作,可以获得一个 DefaultSqlSession 对象。通过它就可以执行 sql 语句了。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。通过SqlSession 实例来直接执行在 xml 文件中的语句,以 selectOne 方法为例,进入该方法后发现,最终会调用到 selectList 方法。

如下图所示,在 configuration 中可以通过 getMappedStatement 方法获取 MappedStatement 对象,然后再调用 executor.query 方法,调用 query 方法之前会执行 wrapCollection 方法,用于保存 sql 语句中传入的参数。

1722767863125.png

通过 getBoundSql 方法可以获取要执行的 sql, 如果开启缓存,那么 Cachekey 就需要执行缓存策略,通过 id, offset, limit,sql 组成一个唯一的 key ,然后调用 query 方法,执行完查询后,最后调用 ResultSetHandler.handleCursorResultSets 来封装结果集并返回。其具体的执行流程如下图所示:

98de5fee1d9d981a5528b07b8a959db.png

6 总结

在本文中,主要介绍了 spring 如何使用其扩展点来注入 mapper,以及 myabtis 的配置文件加载流程以及 sql 的执行流程, 加深了对 mybatis 的架构理解。