Mybatis源碼解析,一步一步從淺入深(三):實例化xml配置解析器(XMLConfigBuilder)


 

在上一篇文章:Mybatis源碼解析,一步一步從淺入深(二):按步驟解析源碼 ,中我們看到

  代碼:XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);

  使用讀取的configuration.xml字符輸入流作為參數,使用XMLConfigBuilder類的三個參數的構造器實例化一個xml配置解析器(XMLConfigBuilder),並且 environment, properties的值為null。並且在這個過程中會實例化一個非常重要的類Configuration的對象。那么接下來就看一下XMLConfigBuilder的實例化過程

一,首先看下這個三個參數的構造器源碼:

  

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

這里又通過this關鍵字,調用了XMLConfigBuilder類的另外一個三個參數的私有構造方法:

  這個構造器的參數XPathParser parser就等於上一個構造器中的new XPathParser(reader, true, props, new XMLMapperEntityResolver());

  XPathParser 是一個xml解析器,同時reader就是configuration.xml字符輸入流,而props等於null;

    具體的XPathParser xml解析器的實例化過程就不再這里詳細描述了,大家有需要,我再領寫一篇文章。

  同時environment, properties的值為null

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

在這個構造器中類XMLConfigBuilder使用super關鍵字調用了父類BaseBuilder的使用Configuration作為參數的構造方法:

  

public class XMLConfigBuilder extends BaseBuilder
  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

二,Configuration 的初始化

  從第一步我們知道,類XMLConfigBuilder使用super關鍵字調用了父類BaseBuilder的使用Configuration作為參數的構造方法。

  代碼:super(new Configuration());

  這里new Configuration()實例化了一個實例,並且類Configuration就是上文提到的非常重要的類。首先來瀏覽一下Configuration類的大致結構。

  

public class Configuration {

  protected Environment environment;

  ......
  
  protected Properties variables = new Properties();
  
  ......
  //類型別名注冊表
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  //語言驅動注冊表
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  ......

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

}

  誠然,這個類被我簡化了,忽略了一些其他的屬性和方法,當然這些被忽略的屬性和方法中,也包含重要的屬性和方法,這些重要的屬性和方法將在后續的文章中逐一說明。

  1,首先看一個類TypeAliasRegistry (類型別名注冊表),其實這個類封裝了一個HashMap,鍵為類的別名,值為類對象。看一下簡化的源碼:

    

public class TypeAliasRegistry {

  private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    ......
  }
    
  @SuppressWarnings("unchecked")
  // 獲取類對象
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) return null;
      String key = string.toLowerCase(Locale.ENGLISH); // issue #748
      Class<T> value;
      if (TYPE_ALIASES.containsKey(key)) {
        value = (Class<T>) TYPE_ALIASES.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

  .....
  //保存類對象
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) throw new TypeException("The parameter alias cannot be null");
    String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

  ......

}

    1,TypeAliasRegistry類中有一個final 修飾的TYPE_ALIASES  HashMao,Map中以別名-類對象的鍵-值對保存別名和類對象。

    2,TypeAliasRegistry類中有一個保存別名-類對象的方法registerAlias,參數正式String  類型的別名和Class 類型的類對象,在這個方法中首先檢查Map TYPE_ALIASES中是否已經存在,如果存在就會拋出TypeException異常。如果不存在就將別名-類對象保存到Map TYPE_ALIASES中。另外一個方法resolveAlias是根據別名獲取類對象或者使用類加載器加載一個類返回類對象。

    3,TypeAliasRegistry類中還有一個構造方法,在這個構造方法中,對常用的類對象和別名調用registerAlias方法,進行了初始化。

  2,了解了TypeAliasRegistry中的內容后,就該回頭看看Configuration 類的構造方法了。

    

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    .....
  }

  顯而易見,在Configuration類的構造方法中,也向TypeAliasRegistry(類型別名注冊表)中添加了一些別名和類對象。

  3,除去了解了Configuration類的構造方法都做了什么事情外,還要知道

    protected Environment environment;

    protected Properties variables = new Properties();
    //類型別名注冊表    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();    //語言驅動注冊表    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

   這幾個重要的屬性,特別是Properties variables。因為在XPathParser類中也有這個屬性,千萬不能搞混了。

    

public class XPathParser {
  ......
  private Properties variables;
  ......
}

三,明白了Configuration在的大概結構和實例化的時候都做了什么事情之后,我們就接着第一點的末尾繼續跟蹤代碼。

  1,在第一點中我們提到:類XMLConfigBuilder使用super關鍵字調用了父類BaseBuilder的使用Configuration作為參數的構造方法:
  

  在BaseBuilder中,對新實例化的Configuration 對象進行了賦值,同時對TypeAliasRegistry進行了賦值。

  2,回到XMLConfigBuilder的構造方法

  

  文章開始的地方我們說過 enviroment和props 是null,所以this.configuration.variables == null,this.environment == null。

  this.parsed  =false;意思是還沒有執行對configration.xml的解析;

  this.parser = parser;這里要注意,這個parser是XPathParser parse。而上一篇文章 Mybatis源碼解析,一步一步從淺入深(二):按步驟解析源碼 中的 parser 是XMLConfigBuilder類的對象。

      

 

 

  到這里實例化xml配置解析器(XMLConfigBuilder)就到此結束了。不要以為XMLConfigBuilder的任務就結束了,no ,no ,no .這才是剛剛開始

 


 原創不易,轉載請聲明出處:https://www.cnblogs.com/zhangchengzi/p/9673715.html 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM