日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網(wǎng)站首頁 編程語言 正文

mybatis源碼之集成springboot原理

作者:xuguofeng2016 更新時間: 2022-07-22 編程語言

本文將結合示例代碼、閱讀源碼,去深入了解mybatis與spring boot集成的底層原理。

示例代碼

示例展示最小化配置。

依賴

只記錄相關內(nèi)容:

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.4</version>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.44</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.20</version>
</dependency>

application.yml配置

只記錄相關內(nèi)容:

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接口

與之前一樣,不記錄。

接口層編寫

只記錄相關內(nèi)容:

@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等包。

自動裝配的實現(xiàn)在mybatis-spring-boot-autoconfigure包里面。

我們知道spring boot的自動裝配原理是在自動裝配包的META-INF下創(chuàng)建spring.factories文件,在文件里面配置EnableAutoConfiguration的配置類全名即可實現(xiàn)自動裝配,我們看一下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

核心內(nèi)容就在MybatisAutoConfiguration這個類。

所以這個類會幫助我們創(chuàng)建SqlSessionFactory和SqlSessionTemplate,而DataSource是由DataSourceAutoConfiguration類來做的(這個暫不討論)。

所以應該從MybatisAutoConfiguration為入口開始分析。

其實基本上所有的第三方庫的集成都是這樣的原理:

  • 一個starter庫
  • 一個autoconfigure庫
  • 一個spring.factories文件,其實這個才是最重要的。至于是一個包還是兩個包,都問題不大,可以實現(xiàn)就行
  • 一個AutoConfiguration類
  • 一個Properties類

至于spring.factories文件被加載的原理,這是spring boot的內(nèi)容,本文不做擴展分析。

源碼分析

MybatisAutoConfiguration類

這個類負責創(chuàng)建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<ConfigurationCustomizer> configurationCustomizers;

  public MybatisAutoConfiguration(
      MybatisProperties properties, // 用戶在application.yml里面的配置參數(shù),后續(xù)詳解
      // 容器里面的Interceptor攔截器,就是說使用@Component標注的Interceptor實現(xiàn)類對象都會在這里,
      // 這是spring的依賴注入特性,
      // 這種機制更便于組件擴展
      ObjectProvider<Interceptor[]> interceptorsProvider,
      // 容器里面的TypeHandler集合,原理同上
      ObjectProvider<TypeHandler[]> typeHandlersProvider,
      ObjectProvider<LanguageDriver[]> languageDriversProvider,
      ResourceLoader resourceLoader,
      ObjectProvider<DatabaseIdProvider> databaseIdProvider,
      // 這個也是一種擴展機制,讓用戶獲取到Configuration對象,然后做操作
      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    // 成員變量賦值
  }

創(chuàng)建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<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();
}

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)) {
    // 這里在調(diào)用用戶的ConfigurationCustomizer自定義擴展邏輯
    for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
      customizer.customize(configuration);
    }
  }
  factory.setConfiguration(configuration);
}

創(chuàng)建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);
  }
}

代碼比較簡單,就是創(chuàng)建SqlSessionTemplate對象。

MybatisProperties配置類

MybatisProperties配置類封裝了用戶的所有mybatis配置,這個類讀取mybatis.開始的配置參數(shù),封裝到自己的屬性中。

類定義:

@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實現(xiàn)類
  • 使用ConfigurationCustomizer的customize方法向Configuration addInterceptor
  • 編寫mybatis主配置文件,使用plugins標簽注冊

@Component標注Interceptor實現(xiàn)類

在之前分析MybatisAutoConfiguration時看過構造方法,其中讓spring注入了一個ObjectProvider<Interceptor[]>對象,這個對象里面是容器里面所有的Interceptor接口的實例對象。

ObjectProvider接口是spring 4.3提供的新特性,是ObjectFactory接口的擴展,專門為注入點設計的,可以讓注入變得更加寬松和更具有可選項。

如果待注入?yún)?shù)的bean為空或有多個時,ObjectProvider就會起作用:

  • 如果注入實例為空時,使用ObjectProvider則避免了強依賴導致的依賴對象不存在異常
  • 如果有多個實例,ObjectProvider的方法會根據(jù)bean實現(xiàn)的Ordered接口或@Order注解指定的先后順序獲取一個Bean。從而了提供了一個更加寬松的依賴注入方式

所以就可以理解這種方法了。

所以不只是使用@Component標注Interceptor實現(xiàn)類,在Configuration配置類中使用@Bean創(chuàng)建bean方式也是可行的。

@Bean
public PrintSqlInterceptor printSqlInterceptor() {
  return new PrintSqlInterceptor();
}

ConfigurationCustomizer的customize方法

MybatisAutoConfiguration的構造方法還注入了ObjectProvider<List<ConfigurationCustomizer>>對象,其原理和前文一樣,我們需要了解的是ConfigurationCustomizer接口:

@FunctionalInterface
public interface ConfigurationCustomizer {

  void customize(Configuration configuration);
}

這是一個函數(shù)接口,customize方法接收一個mybatis的Configuration對象作為參數(shù),可以使用Configuration的addInterceptor(Interceptor)方法注冊插件。

其實不止是插件,其他操作Configuration對象的需求也可以使用這個接口來實現(xiàn)。

原文鏈接:https://blog.csdn.net/xuguofeng2016/article/details/125919307

欄目分類
最近更新