mybatis源码解析(二)初始化SqlSessionFactory对象

上篇我们准备好了debug环境,这篇我们具体深入分析下SqlSessionFactory 的创建

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

1、SqlSessionFactoryBuilder创建SqlSessionFactory对象

调用重载方法

public SqlSessionFactory build(InputStream inputStream) {
 
  return build(inputStream, null, null);
}

创建XMLConfigBuilder对象,调用parse方法解析XML配置文件

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

解析成Configuration对象,然后再调用重载方法,创建SqlSessionFactory对象

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

 

2、分析XMLConfigBuilder的parse方法,解析配置文件

public Configuration parse() {
    if (parsed) { // 第一次进入解析时,会在下面将parsed设置true,保证只能解析一次
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

parseConfiguration(parser.evalNode("/configuration"));

解析配置文件configuration标签,我们主要分析下第一篇中配置的properties,environments,mappers是三个标签

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

1)properties标签解析

解析resource和url方式引入的配置属性,而且如果resource和url同时不为null就会抛出BuilderException,证明只能使用其中的一种。最后将这些属性保存到configuration对象中,例子中配置的这些属性是environments中的数据源属性

private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    Properties defaults = context.getChildrenAsProperties();
    // 解析properties标签是否有resource属性
    String resource = context.getStringAttribute("resource");
    // 解析properties标签是否有url属性
    String url = context.getStringAttribute("url");
    if (resource != null && url != null) {
      throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
    }
    if (resource != null) {
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    parser.setVariables(defaults);
    // 将properties属性保存到configuration对象
    configuration.setVariables(defaults);
  }
}

2)environments标签解析

1、解析transactionManager子标签type类型为JDBC,创建事务工厂,用于创建事务

2、解析dataSource子标签属性type为POOLED,创建数据源工厂,用于创建数据源

3、封装事务工厂和数据源到Environment对象中,set到Configuration对象中

private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    for (XNode child : context.getChildren()) {
      String id = child.getStringAttribute("id");
      if (isSpecifiedEnvironment(id)) {
         // 创建事务工厂 
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        // 创建数据源工厂
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        // 获取数据源
        DataSource dataSource = dsFactory.getDataSource();
        // 通过Builder创建Environment对象
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        // 设置Environment对象到configuration对象中
        configuration.setEnvironment(environmentBuilder.build());
      }
    }
  }
}

3)mappers标签解析

通过package,resource,url,class四种方式加载mapper文件,由于我们配置的是resource方式,通过XMLMapperBuilder加载的配置文件,所以直接分析mapperParser.parse();

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          // *********************resource解析mapper文件逻辑*************************  
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          mapperParser.parse();
          // *********************resource解析mapper文件逻辑*************************  
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

4)mapperParser.parse(); 解析mapper标签

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    // 解析mapper标签,解析sql封装MappedStatement对象保存到configuration中  
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    // 创建Mapper接口的代理工厂类,扫描Mapper接口方法的注解,封装sql
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

1、configurationElement(parser.evalNode("/mapper"));

1)解析获取mapper文件的namespace

2)解析sql(SqlSource 封装)调用栈如下

buildStatementFromContext(list, null);
  statementParser.parseStatementNode();
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
      builder.parseScriptNode();
        sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);

3)将SqlSource和一系列参数封装到MappedStatement,通过 configuration.addMappedStatement(statement);保存到configuration对象中,后续在调用mapper接口方法时,会从这里取出sql执行

2、bindMapperForNamespace(); // 绑定mapper接口和对应的mapper代理类

1)调用栈如下,后续可以通过sqlSession对象根据mapper类型来获取对应的mapper代理对象

configuration.addMapper(boundType);
  mapperRegistry.addMapper(type);
    knownMappers.put(type, new MapperProxyFactory<>(type));

2)下面还有一个很重要的处理逻辑,就是读取mapper接口方法的注解,创建SqlSource对象,最后再封装成MappedStatement对象保存到configuratioin中mappedStatements属性中。

调用栈如下

knownMappers.put(type, new MapperProxyFactory<>(type));
// 在put代理工厂后
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();    
  parseStatement(method);
    SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
      buildSqlSourceFromStrings(strings, parameterType, languageDriver);
        assistant.addMappedStatement(...)

5)至此XMLConfigBuilder的parse方法解析完毕,返回configuration对象,再调用build方法创建DefaultSqlSessionFactory对象,SqlSessionFactory初始化完成

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

 

上一篇:Mybatis - 笔记整理(一)


下一篇:Spring 整合 MyBatis 项目实例