Fork me on GitHub

mybatis-spring-boot-starter自动装配实现

Mybatis-spring-boot-starter的入口

image-20220718024228821

会在springboot启动的时候加载autoconfigure模块定义的自动装配类:MybatisAutoConfiguration
image-20220718024241136

MybatisAutoConfiguration中装配Mybatis的逻辑

SqlSessionFactory的构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 通过SqlSessionFactoryBean来实现 SqlSesionFactory的初始化
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();

factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
// 给factory设置Mybatis的全局配置对象Configuration
applyConfiguration(factory);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}

return factory.getObject();
}

SqlSessionFactory的实例化是通过SqlSessionFactoryBean.getObject()实现的,该类会被注入DataSource对象(负责管理数据库连接池,Session指的是一次会话,而这个会话是在DataSource提供的Connection上进行的)。

在factory.getObject()中,最核心还是调用了buildSqlSessionFactory 去创建了sqlSessionFactory实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
// 创建XMLConfigBuilder对象,读取指定的配置文件
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(),
null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
// 其他方式初始化Configuration全局配置对象
}
// 初始化MyBatis的相关配置和对象,其中包括:
// 扫描typeAliasesPackage配置指定的包,并为其中的类注册别名
// 注册plugins集合中指定的插件
// 扫描typeHandlersPackage指定的包,并注册其中的TypeHandler
// 配置缓存、配置数据源、设置Environment等一系列操作
// ...... 省略配置代码

if (this.transactionFactory == null) {
// 默认使用的事务工厂类
this.transactionFactory = new SpringManagedTransactionFactory();
}

// 根据mapperLocations配置,加载Mapper.xml映射配置文件以及对应的Mapper接口
for (Resource mapperLocation : this.mapperLocations) {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(...);
xmlMapperBuilder.parse();
}
// 最后根据前面创建的Configuration全局配置对象创建SqlSessionFactory对象
return this.sqlSessionFactoryBuilder.build(configuration);
}

SqlSessionFactory.getObject()方法里会根据我们mybatis相关配置(比如上面的mybatis.mapper-locations配置)找到并解析我们的mapper文件,解析出sql与dao方法里的映射、ResultMap与具体实体类的映射等,并放到SqlSessionFactory的Configuration中缓存下来,在后续调用过程中会通过这些信息来匹配jdbc操作。这个过程中会生成Mapper的代理类,执行sql时会走代理拦截方法执行,则不需要实现类就可以直接调用到具体的sql。

SqlSessionTemplate

如果使用手写java代码的方式去操作数据库,则使用的为sqlSessionTemplate,SqlSessionTemplate 是线程安全的,可以在多个线程之间共享使用。SqlSessionTemplate 内部持有一个 SqlSession 的代理对象(sqlSessionProxy 字段),这个代理对象是通过 JDK 动态代理方式生成的;使用的 InvocationHandler 接口是 SqlSessionInterceptor,其 invoke() 方法会拦截 SqlSession 的全部方法,并检测当前事务是否由 Spring 管理。

在MybatisAutoConfiguration自动配置中也去生成了SqlSessionTemplate这个bean对象。

1
2
3
4
5
6
7
8
9
10
11
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
// 返回SqlSessionTemplate bean 用于手写数据库的CRUD操作。
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

MapperFactoryBean和MapperScannerConfigurer

在MybatisAutoConfiguration中注册了AutoConfiguredMapperScannerRegistrar,其实ImportBeanDefinitionRegistrar的实现类,Spring中可以通过 @Import 注解导入的 ImportBeanDefinitionRegistrar 实现类往 BeanDefinitionRegistry 注册 BeanDefinition。这里其实就是去注册了MapperScannerConfigurer 这个BeanDefinitionRegistryPostProcessor的实现。
image-20220718024301164

BeanDefinitionRegistryPostProcessor是一个特殊的接口,继承了BeanFactoryPostProcessor接口,提供了注册bean定义的能力,这里MapperScannerConfigurer扫描所有的@Mapper注解的Maapper类,在扩展点方法中去注册bean定义都为MapperFactoryBean接口,这些Bean在实例化时会因为是FactoryBean,而调用getObject方法寻找Mapper接口的动态代理缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}

ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
// 设置扫描的Bean都是MapperFactoryBean接口的子类
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
// scan扫描@Mapper注解的接口 注册bean定义。
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}s

MapperFactoryBean的getObject()方法是从Configuration配置对象中寻找对应Mapper接口的代理类缓存,没有的话会创建。 这个SpringBean在注入的时候就是对应的代理类了。内部会转发到sqlSession、SqlExecutor去执行sql。

1
2
3
4
5
@Override
public T getObject() throws Exception {
// getSqlSession.getMapper 会从全局唯一的配置对象中寻找接口类对应的Mapper代理
return getSqlSession().getMapper(this.mapperInterface);
}s
-------------本文结束感谢您的阅读-------------

本文标题:mybatis-spring-boot-starter自动装配实现

文章作者:夸克

发布时间:2020年12月18日 - 02:12

最后更新:2022年07月18日 - 02:07

原始链接:https://zhanglijun1217.github.io/2020/12/18/mybatis-spring-boot-starter自动装配实现/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。