【Mybatis-源码解析】1. 整合spring boot后启动流程

777 阅读5分钟

Mybatis 启动流程

整体流程

流程图

graph TD
整体流程 --> 通过factories加载MybatisAutoConfiguration自动配置类 --> MybatisAutoConfiguration加载 --> 注入SqlSessionFactory --> 
注入SqlSessionTemplate --> checkConfigFileExists校验配置文件是否存在 --> 结束

开始加载MybatisAutoConfiguration --> 加载MybatisProperties --> 加载Interceptor --> 加载ResourceLoader --> 加载DatabaseIdProvider --> ConfigurationCustomizer --> MybatisAutoConfiguration加载过程完毕

开始注入SqlSessionFactory --> 构建SqlSessionFactoryBean --> 通过getObject方法获取方法获取SqlSessionFactory --> SqlSessionFactory注入完毕

文本整体流程

  1. spring MybatisAutoConfiguration 包中配置 spring.factories 自动加载 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
    1. MybatisAutoConfiguration 依次加载
      1. MybatisProperties: 读取spring配置:读取配置为 mybatis下配置
      2. Interceptor: 拦截器
      3. ResourceLoader: 资源加载器 (使用的 AnnotationConfigServletWebApplicationContext)
      4. DatabaseIdProvider: 数据库Id提供程序
      5. ConfigurationCustomizer: 配置自定义程序提供程序
    2. 注入SQL上下文工厂类(SqlSessionFactory)
      1. 通过上述配置 构建 SqlSessionFactoryBean
      2. 调用SqlSessionFactoryBean.getObject()方法获取方法获取SqlSessionFactory
        1. 根据上述配置、配置configuration对象
          • 设置对象工厂
          • 对象代理工厂
          • vfs
          • 解析别名包、解析别名类
          • 设置拦截器
          • 解析类型解析器包、解析类型解析器
          • 设置数据库ID生成器
          • 设置缓存
          • 设置事务工厂
          • 通过Xpath解析MAPPER.XML文件(将数据存储在configuration对象中)

MybatisPropertiespes 属性

变量名变量类型含义
configLocationStringxml配置文件的位置
mapperLocationsString[]MyBatis映射程序文件(mapper)的位置
typeAliasesPackageString注册的 类型的包名(多个用“,“分隔)
typeHandlersPackageString注册的 类型处理器所在的包(多个用“,“分隔)
checkConfigLocationboolean是否对Myabtis.xml配置文件进行检查
executorTypeExecutorType执行器类型
configurationPropertiesPropertiesMyBatis的配置(以Map形式存在)
configurationConfiguration自定义默认设置的配置对象

整体流程之 SqlSessionFactory 注入

  • 源码解析
  // 主方法
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    // 创建一个新的sql会话工厂
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    
    // 设置数据源属性
    factory.setDataSource(dataSource);
    
    // 设置vfx 默认使用Spring的SpringBootVFS类
    factory.setVfs(SpringBootVFS.class);
    
    // 如果配置了configLocation 设置 MyBatis xml配置文件的位置。到工厂中
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    
    // 在配置中获取 Configuration 对象 如果 获取不到 创建一个新的 Configuration 对象 (设置常用别名)
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    
    // 如果 ConfigurationCustomizer 不为空 customizer 设置 ConfigurationCustomizer
    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);
    }
    
    // 如果数据库Id提供程序不为空,将数据库ID提供程序写入工厂中
    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());
    }
    
    // 如果资源解析不为空,那么将资源写入MapperLocations(具体mapper文件)
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }

    return factory.getObject();
  }
  
  // 获取对象方法
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      // 保证数据源 和 sqlSessionFactoryBuilder 不为空
      notNull(dataSource, "Property 'dataSource' is required");
      notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
      // 保证configuration和configLocation都不为空
      state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),"Property 'configuration' and 'configLocation' can not specified with together");

      // 构建sqlSession工厂
      this.sqlSessionFactory = buildSqlSessionFactory();
    }

    return this.sqlSessionFactory;
  }

注入SqlSessionFactory流程中 构建SqlSessionFactory对象

protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    // 声明Mybatis配置
    Configuration configuration;

    // 声明xml配置生成器
    XMLConfigBuilder xmlConfigBuilder = null;
    // 如果配置不为空
    if (this.configuration != null) {
       // 赋值 当前Mybatis配置信息
      configuration = this.configuration;
      // 如果配置的变量为空 写入configurationProperties 
      if (configuration.getVariables() == null) {
        configuration.setVariables(this.configurationProperties);
      } 
      // 如果配置的变量不为空并且configurationProperties不为空 将configurationProperties全部put进去
      else if (this.configurationProperties != null) {
        configuration.getVariables().putAll(this.configurationProperties);
      }
    } 
    // 如果配置为空但是本地配置不为空 创建一个xml配置解析器 并读取mybatis的xml的配置类
    else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } 
    // 如果配置和本地配置都为空 创建一个新的配置类(使用默认配置) 并设置configurationProperties属性
    else {
      configuration = new Configuration();
      if (this.configurationProperties != null) {
        configuration.setVariables(this.configurationProperties);
      }
    }

    // 如果对象工厂不为空 配置中设置 对象工厂
    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

    // 如果对象代理工厂不为空 设置 代理对象工厂
    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    // 如果 vfs 不为空 设置 vfs
    if (this.vfs != null) {
      configuration.setVfsImpl(this.vfs);
    }

    // 如果扫描的别名包不为空
    if (hasLength(this.typeAliasesPackage)) {
      // 将","隔开的字符串转为数组
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      // 循环每个包 将别名注册到配置中
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
      }
    }

    // 如果别名类型不为空 将别名注入到配置中
    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
      }
    }

    // 如果拦截器不为空 配置中设置拦截器
    if (!isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
      }
    }

    // 如果配置了type-handlers-package属性 则加载包下的所有路径的mapper文件
    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
      }
    }

    // 如果typeHandlers类不为空 将 typeHandlers 类加载到配置中
    if (!isEmpty(this.typeHandlers)) {
      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
      }
    }

    // 如果 databaseIdProvider 类不为空 将 databaseIdProvider 类加载到配置中
    if (this.databaseIdProvider != null) {
      try {
     configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    // 如果 需要缓存 类不为空 将 缓存 类加载到配置中
    if (this.cache != null) {
      configuration.addCache(this.cache);
    }

    // 如果xml配置解析器不是空的 初始化xml配置解析器
    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    // 如果事务工厂是空的 设置一个事务工厂(SpringManagedTransactionFactory)
    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

    // 设置环境
    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

    // 如果 mapper 资源不为空  
    if (!isEmpty(this.mapperLocations)) {
       // 循环 mapper 文件
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          // 使用 mapper 构建器获取 mapper 中的 sql 语句
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),configuration, mapperLocation.toString(), configuration.getSqlFragments());
          // 使用Xpath解析XML文件 解析每个节点 之后将解析好的节点存储到:configuration的各个属性中
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }
    
    // 最终构建sqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

整体流程之 注册SqlSessionTemplate

  • 源码解析
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  // 属性中获取执行器类型
  ExecutorType executorType = this.properties.getExecutorType();
  // 如果执行器类型不为空 根据执行器和sqlSessionFactory创建SqlSessionTemplate
  if (executorType != null) {
    return new SqlSessionTemplate(sqlSessionFactory, executorType);
  } 
  // 如果执行器为空 根据sqlSessionFactory创建SqlSessionTemplate(执行器类型设置为默认类型SIMPLE)
  else {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}

注册SqlSessionTemplate流程中 new SqlSessionTemplate

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {

  // 确保sq会话工厂、执行器类型不为空
  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  notNull(executorType, "Property 'executorType' is required");
  
  // 设置sql会话工厂
  this.sqlSessionFactory = sqlSessionFactory;
  // 设置执行器类型
  this.executorType = executorType;
  // 设置异常转换器
  this.exceptionTranslator = exceptionTranslator;
  // 设置sql会话代理,创建代理实例对象
  this.sqlSessionProxy = (SqlSession) newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class },
      new SqlSessionInterceptor());
}

整体流程之 checkConfigFileExists(校验配置文件是否存在)

  • 源码解析
   // 配置中有mybatis.XML配置 && mybatis.XML配置路径不为空
   if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
      获取 通过资源加载器获取到mybatis.xml配置
      Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
      // 判断resource是否存在,如果不存在抛出异常
      Assert.state(resource.exists(), "Cannot find config location: " + resource
          + " (please add config file or check your Mybatis configuration)");
    }

文章链接