25-Mybatis和SpringBoot集成原理

Mybatis和SpringBoot的集成

  • 前面介紹了MyBatis和Spring的集成,再來看和SpringBoot的整合應該會比較好理解了

一、MyBatis和SpringBoot

  • 和SpringBoot集成MyBatis的話,主要需要增加下面這個依賴:
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
  • mybatis-spring-boot-starter本身沒有提供Jar包和Class文件,只有引入的依賴,如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot</artifactId>
    <version>1.3.2</version>
  </parent>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <name>mybatis-spring-boot-starter</name>
  
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <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>
  
</project>
  • 這個包主要會引入mybatis、mybatis-spring以及mybatis-spring-boot-autoconfigure(其他的不是關鍵),因此MyBatis和SpringBoor集成的關鍵是mybatis-spring-boot-autoconfigure 這個包;

  • 從下面的截圖看出,mybatis-spring-boot-autoconfigure本身比較簡單,它通過spring.factories註冊一個配置類,本身也就兩三個類文件,關鍵還是在 MybatisAutoConfiguration 這個配置類。只要SpringBoot項目開啓了 @EnableAutoConfiguration ,那麼該配置文件就會自動裝配,而SpringBoot的啓動註解 @SpringBootApplication 就默認啓用了 @EnableAutoConfiguration;

在這裏插入圖片描述

二、MybatisAutoConfiguration

  • MybatisAutoConfiguration 是MyBatis和SpringBoot集成的關鍵

2.1 屬性

/**
 * MyBatis的自動配置,提供 SqlSessionFactory 和 SqlSessionTemplate 
 * 
 * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a
 * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.
 *
 * 如果使用了MapperScan或者配置文件配置了接口的包,就會使用這些,如果沒有,那麼就會在自動配置的包下去掃描
 * If {@link org.mybatis.spring.annotation.MapperScan} is used, or a
 * configuration file is specified as a property, those will be considered,
 * otherwise this auto-configuration will attempt to register mappers based on
 * the interface definitions in or under the root auto-configuration package.
 *
 */
 
//有DataSource這個Bean,並且classpath下有SqlSessionFactory和SqlSessionFactoryBean時這個配置類纔會生效,
//並且會在DataSourceAutoConfiguration之後裝配
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

  //配置
  private final MybatisProperties properties;

  private final Interceptor[] interceptors;

  private final ResourceLoader resourceLoader;

  private final DatabaseIdProvider databaseIdProvider;

  private final List<ConfigurationCustomizer> configurationCustomizers;

  //構造方法
  public MybatisAutoConfiguration(MybatisProperties properties,
                                  ObjectProvider<Interceptor[]> interceptorsProvider,
                                  ResourceLoader resourceLoader,
                                  ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                  ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    this.properties = properties;
    this.interceptors = interceptorsProvider.getIfAvailable();
    this.resourceLoader = resourceLoader;
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
  }

  //初始化之前,檢查配置文件是否存在
  @PostConstruct
  public void checkConfigFileExists() {
    if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
      Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
      Assert.state(resource.exists(), "Cannot find config location: " + resource
          + " (please add config file or check your Mybatis configuration)");
    }
  }



}

2.2 配置SqlSessionFactory

  • 如果沒有SqlSessionFactory對應的Bean,則裝配一個,也是通過SqlSessionFactoryBean,和spring集成mybatis幾乎一樣
 
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    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()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    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 (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }

    return factory.getObject();
  }

2.3 配置SqlSessionTemplate

  • 如果沒有 SqlSessionTemplate 對應的Bean,則裝配一個
  @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);
    }
  }

2.4 配置MapperFactoryBean

  • 看下自動配置MapperFactoryBean,如果用戶使用了@MapperScan 註解,那麼就會註冊該Bean,如果用戶沒有使用,那麼就不會有這個Bean,那麼此時就會導入AutoConfiguredMapperScannerRegistrar,在AutoConfiguredMapperScannerRegistrar裏面完成 @Mapper 接口的掃描
  /**
   * {@link org.mybatis.spring.annotation.MapperScan} ultimately ends up
   * creating instances of {@link MapperFactoryBean}. If
   * {@link org.mybatis.spring.annotation.MapperScan} is used then this
   * auto-configuration is not needed. If it is _not_ used, however, then this
   * will bring in a bean registrar and automatically register components based
   * on the same component-scanning path as Spring Boot itself.
   */
  @org.springframework.context.annotation.Configuration
  @Import({ AutoConfiguredMapperScannerRegistrar.class })
  @ConditionalOnMissingBean(MapperFactoryBean.class)
  public static class MapperScannerRegistrarNotFoundConfiguration {

    @PostConstruct
    public void afterPropertiesSet() {
      logger.debug("No {} found.", MapperFactoryBean.class.getName());
    }
  }
  • AutoConfiguredMapperScannerRegistrar : 掃描@MapperScan 註解的類。
  /**
   * 掃描MapperScan
   * This will just scan the same base package as Spring Boot does. If you want  more power, you can explicitly use
   * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed
   * mappers working correctly, out-of-the-box, similar to using Spring Data JPA repositories.
   */
  public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private BeanFactory beanFactory;

    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      
      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }
        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);

        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(packages));
      } catch (IllegalStateException ex) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
      }
    }
  }

三、MybatisProperties

  • MybatisProperties是MyBatis和SpringBoot整合之後的配置文件,如下:
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

  //配置前綴
  public static final String MYBATIS_PREFIX = "mybatis";

  //MyBatis配置文件路徑
  private String configLocation;

  //xml映射文件路徑
  private String[] mapperLocations;

  //別名映射文件
  private String typeAliasesPackage;

  //類型處理器包路徑
  private String typeHandlersPackage;

  //executorType 包括 batch / reuse / simple
  private ExecutorType executorType;

  //直接設置一個配置對象
  private Properties configurationProperties;
}
  • 和SpringBoot集成後MyBatis的配置:

在這裏插入圖片描述

四、小結

  • Mybatis和SpringBoot集成原理本身沒有太多新東西,主要是利用了spring.factories機制 和 @ConditionalOnMissingBean 來實現,前者來導入一個配置類,後者當容器不存在某個Bean的時候則自動裝配一個Bean ,至於@Import和ImportBeanDefinitionRegistrar等都是Spring本身的功能機制了。

  • 代碼:SpringBoot集成MyBatis

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章