Mybatis是如何解析配置文件的?看完終於明白了

在之前文章中,咱們把Mybatis源碼閱讀的整個流程梳理了一遍。今天,咱們來詳細聊聊,Mybatis是如何解析配置文件的。java

這是今天分析的流程圖:sql

圖片

仍是從案例開始。apache

demo案例

   public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        SqlSession sqlSession = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();

            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            System.out.println(userMapper.selectById(1));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSession.close();
        }
    }

見證奇蹟

SqlSessionFactoryBuilder開始。session

SqlSessionFactoryBuilder類

org.apache.ibatis.session.SqlSessionFactoryBuilder

該類裏全是build方法各類重載。mybatis

圖片


//這個方法啥也沒幹  
public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, nullnull);
 }

最終來到另一個build方法裏:app

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //建立一個XMLConfigBuilder對象  
      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.
      }
    }
  }

XMLConfigBuilder類

該類的構造方法重載:ide

圖片

首先進入:源碼分析

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment,     
         props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}

build(parser.parse());中的parser.parse();ui

mybatis-config.xml在哪裏解析的呢?

請看下面這個方法:this

//該方法返回一個Configuration對象
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  //關鍵點
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

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

終於看到開始解析配置文件了:

圖片

進入方法parseConfiguration。

  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);
    }
  }

這裏就是把mybatis-config.xml內容解析,而後設置到Configuration對象中。

那麼咱們定義的Mapper.xml是在哪裏解析的呢?

咱們的Mapper.xml在mybatis-config.xml中的配置是這樣的:

<mapper>使用方式有如下四種:

//1使用類路徑
<mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
   <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
//2使用絕對url路徑
<mappers>
   <mapper url="file:///var/mappers/AuthorMapper.xml"/>
   <mapper url="file:///var/mappers/BlogMapper.xml"/>
   <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
//3使用java類名
<mappers>
   <mapper class="org.mybatis.builder.AuthorMapper"/>
   <mapper class="org.mybatis.builder.BlogMapper"/>
   <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

//4自動掃描包下全部映射器
<mappers>
   <package name="org.mybatis.builder"/>
</mappers>

繼續源碼分析,咱們在上面mybatis-config.xml解析中能夠看到:

圖片

咱們不妨進入這個方法看看:

 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");
          //使用java類名
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
             //根據文件存放目錄,讀取XxxMapper.xml
            InputStream inputStream = Resources.getResourceAsStream(resource);
             //映射器比較複雜,調用XMLMapperBuilder
            //注意在for循環裏每一個mapper都從新new一個XMLMapperBuilder,來解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          //使用絕對url路徑
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //映射器比較複雜,調用XMLMapperBuilder
            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.");
          }
        }
      }
    }
  }

這裏剛剛和咱們的上面說的<mapper>使用的方式徹底是如出一轍的。

到這裏,配置文件mybatis-config.xml和咱們定義映射文件XxxMapper.xml就所有解析完成。

回到SqlSessionFactoryBuilder類

前面講到了XMLConfigBuilder中的parse方法,並返回了一個Configuration對象。

build(parser.parse());

這個build方法就是傳入一個Configuration對象,而後構建一個DefaultSqlSession對象。

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

繼續回到咱們的demo代碼中這一行代碼裏:

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

這一行代碼就至關於:

SqlSessionFactory sqlSessionFactory = new new DefaultSqlSessionFactory();

到這裏,咱們的整個流程就搞定了。

相關文章
相關標籤/搜索