前面文档(Mybatis源码解析-快速一览)我简单快速的描述了Mybatis的一个整体的源码流程,本文主要聊一下Mybatis一个热门的面试题:MyBatis接口是如何执行SQL的?
网上很多都是基于纯粹mybatis的,我这里主要还是出发点在我们常用的SpringBoot项目中,是如何让我们只通过接口就能去执行到对应的SQL的。
spring为我们整合了很多第三方的starter自动装配来用于快速集成,mybatis自然也不例外,我们分析的入口也就是基于mybatis-spring-boot-starter为例,这里基于当前最新的依赖版本2.1.4为例进行分析。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
这个包里面有啥?
是一个空包,只是用来指定一些依赖(这是spring官方提供的starter的一个规范,具体自动配置其实都在
mybatis-spring-boot-autoconfigure里面)。
这里面引入了3个比较重要的包:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>
这三个包的作用分别是:
- mybatis-spring-boot-starter:只是负责整合依赖关系,类似于parent。
- mybatis-spring-boot-autoconfigure:只是负责自动配置的工作。
- mybatis-spring:负责spring和mybatis结合的核心部分,重点也就是这个部分。
我们先看自动配置(mybatis-spring-boot-autoconfigure-2.1.4.jar包内),这里面选中的是关键的类。
-
先看spring.factories:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration -
可以见得重点是
MybatisAutoConfiguration,该类实现了mybatis的自动配置,而具体的配置可选项其实就是MybatisProperties的内容。 -
我们看看这块代码:
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean { private final MybatisProperties properties; ... @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); //☆ 中间无非一些配置工作,将properties以及一些datasource相关的数据设置进SqlSessionFactoryBean对象。 return factory.getObject(); } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {...} @Configuration @Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class}) @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } } }- 可以看到这里创建了
SqlSessionFactory和SqlSessionTemplate这2个核心的bean,而构建SqlSessionFactory的核心是靠SqlSessionFactoryBean这个类的,而这个类是在mybatis-spring-2.0.6.jar包下。
- 可以看到这里创建了
-
我们知道在纯Spring和Mybatis的场景下,我们是这么获取Mapper来使用的:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); XXMapper mapper = sqlSession.getMapper(XXMapper.class);只不过,这部分
mybatis-config.xml的配置现在交给spring进行自动装配了。sqlSessionFactory的构建工作是由SqlSessionFactoryBuilder来构建的,所以springboot中也不例外,唯一不同的是:上述方法是用的SqlSessionFactory.build(Reader reader)方法,而springboot自动配置是使用的SqlSessionFactoryBuilder.build(Configuration config),configuration对象是我们自己创建好了。// 这里创建了SqlSessionFactoryBuilder,调用mybatis的builder来创建SqlSessionFactory private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); private SqlSessionFactory sqlSessionFactory; /** * 只挑选了核心的方法 */ @Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; } @Override public void afterPropertiesSet() throws Exception { ... this.sqlSessionFactory = buildSqlSessionFactory(); } protected SqlSessionFactory buildSqlSessionFactory() throws Exception { final Configuration targetConfiguration; XMLConfigBuilder xmlConfigBuilder = null; // 构建configuration if (this.configuration != null) { targetConfiguration = this.configuration; if (hasLength(this.typeAliasesPackage)) { } if (!isEmpty(this.typeAliases)) { } if (!isEmpty(this.plugins)) { } if (hasLength(this.typeHandlersPackage)) { } if (!isEmpty(this.typeHandlers)) { } targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler); Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache); if (this.mapperLocations != null) { } return this.sqlSessionFactoryBuilder.build(targetConfiguration); } }
上面这是解析过程,目的就是创建SqlSessionFactory,其逻辑对应 Mybatis源码解析-快速一览的解析过程,这里就不细说了。
自动配置完成后,容器中会注入SqlSessionTemplate的实例bean(一个线程安全的SqlSession实现,主要基于ThreadLocal)。并且我们的mapper.xml里面的内容会解析并保存到configuration的mappedStatements的一个map中,以及将mapper.xml里
<mapper namespace="work.lollipops.demo.mybatis.mapper.TB1Mapper">
解析配置到mapperRegistry的knownMappers(也是一个map)里面。
这2个东西很有用,mybatis所有的方法执行都将主要围绕这2个打交道。