浅析MybatisAutoConfiguration流程

本文主要讲述springboot整合mybatis的自动配置流程。

1. MybatisAutoConfiguration

MybatisAutoConfiguration是一个配置类,主要是注入SqlSessionTemplate,SqlSessionFactory,以及注册@Mapper标注的类。

      @Bean
      @ConditionalOnMissingBean
      public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    
    
      	// spring整合mybatis,以SqlSessionFactoryBean替代SqlSessionFactoryBuilder。
        SqlSessionFactoryBean  factory = new SqlSessionFactoryBean();
        // 忽略一些默认配置,最后返回一个SqlSessionFactory
        .....
        return factory.getObject();
      }
      
      @Bean
      @ConditionalOnMissingBean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    
    
      	// 未配置ExecutorType,则使用默认的ExecutorType.SIMPLE
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
    
    
          return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
    
    
          // spring整合mybatis,以SqlSessionTemplate替代SqlSession
          return new SqlSessionTemplate(sqlSessionFactory);
        }
      }
      
      @org.springframework.context.annotation.Configuration
      // 扫描@Mapper标注的类
      @Import(AutoConfiguredMapperScannerRegistrar.class)
      @ConditionalOnMissingBean({
    
     MapperFactoryBean.class, MapperScannerConfigurer.class })
      public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    
    }

2. SqlSessionFactoryBean

依照上文调用getObject方法,进入SqlSessionFactoryBean类。

public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    
    
    
	public SqlSessionFactory getObject() throws Exception {
    
    
		// 首次调用sqlSessionFactory==null进入afterPropertiesSet
        if (this.sqlSessionFactory == null) {
    
    
          afterPropertiesSet();
        }
        return this.sqlSessionFactory;
    }
    
    public void afterPropertiesSet() throws Exception {
    
    
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        // 这里要求configuration和configuration二者只能存在一个
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
            "Property 'configuration' and 'configLocation' can not specified with together");

        this.sqlSessionFactory = buildSqlSessionFactory();
    }
}

buildSqlSessionFactorySqlSessionFactoryBuilder里面的build逻辑类似。

	protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    
    

    final Configuration targetConfiguration;
	
    XMLConfigBuilder xmlConfigBuilder = null;
    // 通常使用springboot默认配置,则configuration != null
    if (this.configuration != null) {
    
    
      targetConfiguration = this.configuration;
      ... 
    // 进入这里是自定义了xml配置文件  
    } else if (this.configLocation != null) {
    
    
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
    }else {
    
    
    // 什么都没有配置,则创建一个默认的Configuration
      targetConfiguration = new Configuration();
    }
    // 忽略一些常规配置
    ....
    // 这里设置映射xml文件地址
    if (this.mapperLocations != null) {
    
    
      if (this.mapperLocations.length == 0) {
    
    
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
    
    
      	// 遍历每个mapper.xml文件 
        for (Resource mapperLocation : this.mapperLocations) {
    
    
          if (mapperLocation == null) {
    
    
            continue;
          }
          try {
    
    
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            // 这里执行具体xml解析操作    
            xmlMapperBuilder.parse();
          } catch (Exception e) {
    
    
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
    
    
            ErrorContext.instance().reset();
          }
        }
      }
    } else {
    
    
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }

    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }

3.XMLMapperBuilder

这里是进行解析xml文件,并将解析好的信息存入Configuration对象。

  1. parse
public class XMLMapperBuilder extends BaseBuilder {
    
    
    public void parse() {
    
    
    	// 判断是否解析过
        if (!configuration.isResourceLoaded(resource)) {
    
    
          // 解析里面的<mapper><mapper/>信息,包含select之类的语句解析
          configurationElement(parser.evalNode("/mapper"));
          // 标记解析过的资源,避免重复解析
          configuration.addLoadedResource(resource);
          bindMapperForNamespace();
        }
        // 这里是之前解析失败的,再次尝试解析
		// 解析ResultMap
        parsePendingResultMaps();
        // 解析CacheRef
        parsePendingCacheRefs();
        // 解析Statement
        parsePendingStatements();
   }
}
  1. bindMapperForNamespace
    这个方法主要是将dao接口类与代理进行关联。
    private void bindMapperForNamespace() {
    
    
        String namespace = builderAssistant.getCurrentNamespace();
        if (namespace != null) {
    
    
          Class<?> boundType = null;
          try {
    
    
            boundType = Resources.classForName(namespace);
          } catch (ClassNotFoundException e) {
    
    
          }
          if (boundType != null && !configuration.hasMapper(boundType)) {
    
    
            // 这里是标记xml文件对应的dao类,已经解析过了
            configuration.addLoadedResource("namespace:" + namespace);
            // 这里将dao类绑定对应的代理类
            configuration.addMapper(boundType);
          }
        }
      }

猜你喜欢

转载自blog.csdn.net/qq_34789577/article/details/109082129