讀mybatis源碼之一:mybatis加載配置邏輯

       一、加載配置文件   

String resource = "org/mybatis/example/Configuration.xml"; 
Reader reader = Resources.getResourceAsReader(resource); 
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

       二、 解析配置xml,生成配置類    

     將配置資源文件讀取到Reader,然後傳遞構建,構建的時候,可以見SqlSessionFactoryBuilder裏面有不同的方法,主要是支持參數方式傳遞環境元素和屬性參數,最後歸結還是到下面這個方法:

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

        可以看到是使用XMLConfigBuilder將配置文件信息,轉換成Configuration,主要看parser.parse()

 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }

 可以很清楚的看見,讀取配置文件的/configuration節點,然後分別處理不同的標籤:
 propertiesElement(root.evalNode("properties"));//屬性文件,數據庫驅動配置?
 typeAliasesElement(root.evalNode("typeAliases"));//類型方言,用於結果轉換 
 pluginElement(root.evalNode("plugins"));//插件,可以配置不同時機的攔截器
 objectFactoryElement(root.evalNode("objectFactory"));//對象工廠,可以配置返回對象屬性
 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
 settingsElement(root.evalNode("settings")); //全局變量,緩存,延遲加載等配置
 environmentsElement(root.evalNode("environments")); //環境變量,根據配置構建數據源工廠,事務工廠
 databaseIdProviderElement(root.evalNode("databaseIdProvider"));//數據庫提供標識ID,多數據庫兼容的時候用到
 typeHandlerElement(root.evalNode("typeHandlers"));  //類型處理器,主要是對結果集,類型轉換處理
 mapperElement(root.evalNode("mappers"));//不同的mapper  dao處理

   三、mapperElement處理

   提供多種方式加載mapper文件:package、resource、url、mapperClass
   package,mapperClass直接加載mapper class,提供了註解方式加載配置信息。
   resource、url,通過讀取資源文件的方式加載mapper,最終加載所有配置文件後,還是轉換成mapperClass方式加載。也就是configuration的addMapper,這裏面的邏輯就是註冊mapper。mapperRegistry.addMapper,裏面會對mapper class做一次註解配置加載處理,具體見MapperAnnotationBuilder

 public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

       不同加載方式最終的結果是得到註冊所有的mapper接口類的代理工廠類MapperProxyFactory
 

       配置加載基本完畢,主要配置全局參數,環境變量,處理器。

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