栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

mybatis源码之集成springboot原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

mybatis源码之集成springboot原理

本文将结合示例代码、阅读源码,去深入了解mybatis与spring boot集成的底层原理。

示例代码

示例展示最小化配置。

依赖

只记录相关内容:


  org.mybatis.spring.boot
  mybatis-spring-boot-starter
  2.1.4


  mysql
  mysql-connector-java
  5.1.44


  com.alibaba
  druid
  1.1.20

application.yml配置

只记录相关内容:

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_source_analysis
    username: root
    password: 123456
    druid:
      filters: stat
      maxActive: 5
      initialSize: 5
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECt 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 5
# 此处是mybatis自动装配的最小化配置
# 如果在mapper接口上使用注解方式编写SQL的话,这个配置也是不需要的
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
mapper xml配置文件

与之前一样,不记录。

MybatisConfig配置类
@MapperScan(basePackages = {"org.net5ijy.mybatis.test.mapper"})
@Configuration
public class MybatisConfig {
  // 由于DataSource、SqlSessionFactory和SqlSessionTemplate由spring boot mybatis自动装配,这个类可以空白
}
Mapper接口

与之前一样,不记录。

接口层编写

只记录相关内容:

@RestController
@RequestMapping("/blog")
public class BlogController {

  private BlogMapper blogMapper;

  public BlogController(BlogMapper blogMapper) {
    this.blogMapper = blogMapper;
  }

  // other code
}
启动类
@SpringBootApplication
public class MybatisSpringBootApplication {

  public static void main(String[] args) {
    SpringApplication.run(MybatisSpringBootApplication.class, args);
  }
}
核心原理

我们依赖的mybatis-spring-boot-starter包,而这个包没有实际代码和配置文件,只是引入了mybatis、mybatis-spring、和mybatis-spring-boot-autoconfigure等包。

自动装配的实现在mybatis-spring-boot-autoconfigure包里面。

我们知道spring boot的自动装配原理是在自动装配包的META-INF下创建spring.factories文件,在文件里面配置EnableAutoConfiguration的配置类全名即可实现自动装配,我们看一下mybatis-spring-boot-autoconfigure包的spring.factories文件:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

核心内容就在MybatisAutoConfiguration这个类。

所以这个类会帮助我们创建SqlSessionFactory和SqlSessionTemplate,而DataSource是由DataSourceAutoConfiguration类来做的(这个暂不讨论)。

所以应该从MybatisAutoConfiguration为入口开始分析。

其实基本上所有的第三方库的集成都是这样的原理:

  • 一个starter库
  • 一个autoconfigure库
  • 一个spring.factories文件,其实这个才是最重要的。至于是一个包还是两个包,都问题不大,可以实现就行
  • 一个AutoConfiguration类
  • 一个Properties类

至于spring.factories文件被加载的原理,这是spring boot的内容,本文不做扩展分析。

源码分析 MybatisAutoConfiguration类

这个类负责创建SqlSessionFactory和SqlSessionTemplate对象。

看一下这个类的成员和构造方法:

public class MybatisAutoConfiguration implements InitializingBean {

  private final MybatisProperties properties;
  private final Interceptor[] interceptors;
  private final TypeHandler[] typeHandlers;
  private final LanguageDriver[] languageDrivers;
  private final ResourceLoader resourceLoader;
  private final DatabaseIdProvider databaseIdProvider;
  private final List configurationCustomizers;

  public MybatisAutoConfiguration(
      MybatisProperties properties, // 用户在application.yml里面的配置参数,后续详解
      // 容器里面的Interceptor拦截器,就是说使用@Component标注的Interceptor实现类对象都会在这里,
      // 这是spring的依赖注入特性,
      // 这种机制更便于组件扩展
      ObjectProvider interceptorsProvider,
      // 容器里面的TypeHandler集合,原理同上
      ObjectProvider typeHandlersProvider,
      ObjectProvider languageDriversProvider,
      ResourceLoader resourceLoader,
      ObjectProvider databaseIdProvider,
      // 这个也是一种扩展机制,让用户获取到Configuration对象,然后做操作
      ObjectProvider> configurationCustomizersProvider) {
    // 成员变量赋值
  }
创建SqlSessionFactory
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  // 之前与spring集成时做过分析
  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
  factory.setDataSource(dataSource);
  factory.setVfs(SpringBootVFS.class);
  // 设置mybatis.configLocation配置
  if (StringUtils.hasText(this.properties.getConfigLocation())) {
    factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  }
  // 这个方法要看一下
  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);
  }
  // mapper xml扫描
  if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
    factory.setMapperLocations(this.properties.resolveMapperLocations());
  }
  Set factoryPropertyNames = Stream
      .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors())
      .map(PropertyDescriptor::getName)
      .collect(Collectors.toSet());
  Class 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();
}

private void applyConfiguration(SqlSessionFactoryBean factory) {
  Configuration configuration = this.properties.getConfiguration();
  if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
    configuration = new Configuration();
  }
  if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
    // 这里在调用用户的ConfigurationCustomizer自定义扩展逻辑
    for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
      customizer.customize(configuration);
    }
  }
  factory.setConfiguration(configuration);
}
创建SqlSessionTemplate
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  ExecutorType executorType = this.properties.getExecutorType();
  if (executorType != null) {
    return new SqlSessionTemplate(sqlSessionFactory, executorType);
  } else {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}

代码比较简单,就是创建SqlSessionTemplate对象。

MybatisProperties配置类

MybatisProperties配置类封装了用户的所有mybatis配置,这个类读取mybatis.开始的配置参数,封装到自己的属性中。

类定义:

@ConfigurationProperties(prefix = "mybatis")
public class MybatisProperties {
  // ...
}

支持的配置:

从图中可以看到,这类本身支持的配置并不是很多:

mapperLocations - mapper xml配置文件位置
checkConfigLocation - 是否对主配置文件进行检查,默认false不检查
configLocation - 主配置文件位置
configurationProperties - 主配置扩展属性
defaultScriptingLanguageDriver - 默认的脚本语言模板类
executorType - SqlSessionTemplate的运行模式
typeAliases* - 类型别名
typeHandlersPackage - 类型处理器

其余的以mybatis.configuration.开始的配置封装在MybatisProperties的Configuration对象中,这个对象封装mybatis的核心配置,属性较多。

以mybatis.scripting-language-driver.开始的配置是脚本语言模板类的属性,本文不介绍。

@MapperScan注解原理(略)

在 mybatis源码分析_集成spring源码分析 文中有详细分析,此处略。

插件加载原理 三种方式
  • 使用@Component标注Interceptor实现类
  • 使用ConfigurationCustomizer的customize方法向Configuration addInterceptor
  • 编写mybatis主配置文件,使用plugins标签注册
@Component标注Interceptor实现类

在之前分析MybatisAutoConfiguration时看过构造方法,其中让spring注入了一个ObjectProvider对象,这个对象里面是容器里面所有的Interceptor接口的实例对象。

ObjectProvider接口是spring 4.3提供的新特性,是ObjectFactory接口的扩展,专门为注入点设计的,可以让注入变得更加宽松和更具有可选项。

如果待注入参数的bean为空或有多个时,ObjectProvider就会起作用:

  • 如果注入实例为空时,使用ObjectProvider则避免了强依赖导致的依赖对象不存在异常
  • 如果有多个实例,ObjectProvider的方法会根据bean实现的Ordered接口或@Order注解指定的先后顺序获取一个Bean。从而了提供了一个更加宽松的依赖注入方式

所以就可以理解这种方法了。

所以不只是使用@Component标注Interceptor实现类,在Configuration配置类中使用@Bean创建bean方式也是可行的。

@Bean
public PrintSqlInterceptor printSqlInterceptor() {
  return new PrintSqlInterceptor();
}
ConfigurationCustomizer的customize方法

MybatisAutoConfiguration的构造方法还注入了ObjectProvider>对象,其原理和前文一样,我们需要了解的是ConfigurationCustomizer接口:

@FunctionalInterface
public interface ConfigurationCustomizer {

  void customize(Configuration configuration);
}

这是一个函数接口,customize方法接收一个mybatis的Configuration对象作为参数,可以使用Configuration的addInterceptor(Interceptor)方法注册插件。

其实不止是插件,其他操作Configuration对象的需求也可以使用这个接口来实现。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1024669.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号