Mybatis分析(3)-config文件解析XMLConfigBuilder.parseConfiguration

 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //mybatis解析的主流程邏輯在這里開(kāi)始
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  • 可以看到首先判斷了這個(gè)配置文件有沒(méi)有解析唉侄,只能允許沒(méi)有解析過(guò)的進(jìn)行解析税手。其中調(diào)用了parser.evalNode(“/configuration”)返回根節(jié)點(diǎn)的org.apache.ibatis.parsing.XNode表示,XNode里面主要把關(guān)鍵的節(jié)點(diǎn)屬性和占位符變量結(jié)構(gòu)化出來(lái)箩艺,后面我們?cè)倏戳剐埂H缓笳{(diào)用parseConfiguration根據(jù)mybatis的主要配置進(jìn)行解析,如下所示:
  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);
    }
  }

所有的root.evalNode底層都是調(diào)用XML DOM的evaluate()方法猾骡,根據(jù)給定的節(jié)點(diǎn)表達(dá)式來(lái)計(jì)算指定的 XPath 表達(dá)式,并且返回一個(gè)XPathResult對(duì)象敷搪,返回類型在Node.evalNode()方法中均被指定為NODE。
在詳細(xì)解釋每個(gè)節(jié)點(diǎn)的解析前幢哨,我們先來(lái)粗略看下mybatis-3-config.dtd的聲明:

<?xml version="1.0" encoding="UTF-8" ?>
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

<!ELEMENT databaseIdProvider (property*)>
<!ATTLIST databaseIdProvider
type CDATA #REQUIRED
>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

<!ELEMENT typeAliases (typeAlias*,package*)>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
>

<!ELEMENT typeHandlers (typeHandler*,package*)>

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>

<!ELEMENT objectWrapperFactory EMPTY>
<!ATTLIST objectWrapperFactory
type CDATA #REQUIRED
>

<!ELEMENT reflectorFactory EMPTY>
<!ATTLIST reflectorFactory
type CDATA #REQUIRED
>

<!ELEMENT plugins (plugin+)>

<!ELEMENT plugin (property*)>
<!ATTLIST plugin
interceptor CDATA #REQUIRED
>

<!ELEMENT environments (environment+)>
<!ATTLIST environments
default CDATA #REQUIRED
>

<!ELEMENT environment (transactionManager,dataSource)>
<!ATTLIST environment
id CDATA #REQUIRED
>

<!ELEMENT transactionManager (property*)>
<!ATTLIST transactionManager
type CDATA #REQUIRED
>

<!ELEMENT dataSource (property*)>
<!ATTLIST dataSource
type CDATA #REQUIRED
>

<!ELEMENT mappers (mapper*,package*)>

<!ELEMENT mapper EMPTY>
<!ATTLIST mapper
resource CDATA #IMPLIED
url CDATA #IMPLIED
class CDATA #IMPLIED
>

<!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>

從上述DTD可知赡勘,mybatis-config文件最多有11個(gè)配置項(xiàng),分別是properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?捞镰,但是所有的配置都是可選的闸与,這意味著mybatis-config配置文件本身可以什么都不包含。因?yàn)樗械呐渲米詈蟊4娴給rg.apache.ibatis.session.Configuration中岸售,所以在詳細(xì)查看每塊配置的解析前践樱,我們來(lái)看下Configuration的內(nèi)部完整結(jié)構(gòu):


public class Configuration {

  protected Environment environment;
  // 允許在嵌套語(yǔ)句中使用分頁(yè)(RowBounds)。如果允許使用則設(shè)置為false凸丸。默認(rèn)為false
  protected boolean safeRowBoundsEnabled;
  // 允許在嵌套語(yǔ)句中使用分頁(yè)(ResultHandler)拷邢。如果允許使用則設(shè)置為false。
  protected boolean safeResultHandlerEnabled = true;
  // 是否開(kāi)啟自動(dòng)駝峰命名規(guī)則(camel case)映射屎慢,即從經(jīng)典數(shù)據(jù)庫(kù)列名 A_COLUMN 到經(jīng)典 Java 屬性名 aColumn 的類似映射瞭稼。默認(rèn)false
  protected boolean mapUnderscoreToCamelCase;
  // 當(dāng)開(kāi)啟時(shí)忽洛,任何方法的調(diào)用都會(huì)加載該對(duì)象的所有屬性。否則环肘,每個(gè)屬性會(huì)按需加載欲虚。默認(rèn)值false (true in ≤3.4.1)
  protected boolean aggressiveLazyLoading;
  // 是否允許單一語(yǔ)句返回多結(jié)果集(需要兼容驅(qū)動(dòng))。
  protected boolean multipleResultSetsEnabled = true;

  // 允許 JDBC 支持自動(dòng)生成主鍵悔雹,需要驅(qū)動(dòng)兼容复哆。這就是insert時(shí)獲取mysql自增主鍵/oracle sequence的開(kāi)關(guān)。注:一般來(lái)說(shuō),這是希望的結(jié)果,應(yīng)該默認(rèn)值為true比較合適腌零。
  protected boolean useGeneratedKeys;

  // 使用列標(biāo)簽代替列名,一般來(lái)說(shuō),這是希望的結(jié)果
  protected boolean useColumnLabel = true;

  // 是否啟用緩存
  protected boolean cacheEnabled = true;

  // 指定當(dāng)結(jié)果集中值為 null 的時(shí)候是否調(diào)用映射對(duì)象的 setter(map 對(duì)象時(shí)為 put)方法寂恬,這對(duì)于有 Map.keySet() 依賴或 null 值初始化的時(shí)候是有用的。
  protected boolean callSettersOnNulls;

  // 允許使用方法簽名中的名稱作為語(yǔ)句參數(shù)名稱莱没。 為了使用該特性初肉,你的工程必須采用Java 8編譯,并且加上-parameters選項(xiàng)饰躲。(從3.4.1開(kāi)始)
  protected boolean useActualParamName = true;

  //當(dāng)返回行的所有列都是空時(shí)牙咏,MyBatis默認(rèn)返回null。 當(dāng)開(kāi)啟這個(gè)設(shè)置時(shí)嘹裂,MyBatis會(huì)返回一個(gè)空實(shí)例妄壶。 請(qǐng)注意,它也適用于嵌套的結(jié)果集 (i.e. collectioin and association)寄狼。(從3.4.2開(kāi)始) 注:這里應(yīng)該拆分為兩個(gè)參數(shù)比較合適, 一個(gè)用于結(jié)果集丁寄,一個(gè)用于單記錄。通常來(lái)說(shuō)泊愧,我們會(huì)希望結(jié)果集不是null伊磺,單記錄仍然是null
  protected boolean returnInstanceForEmptyRow;

  // 指定 MyBatis 增加到日志名稱的前綴。
  protected String logPrefix;

  // 指定 MyBatis 所用日志的具體實(shí)現(xiàn)删咱,未指定時(shí)將自動(dòng)查找屑埋。一般建議指定為slf4j或log4j
  protected Class <? extends Log> logImpl;

  // 指定VFS的實(shí)現(xiàn), VFS是mybatis提供的用于訪問(wèn)AS內(nèi)資源的一個(gè)簡(jiǎn)便接口
  protected Class <? extends VFS> vfsImpl;

  // MyBatis 利用本地緩存機(jī)制(Local Cache)防止循環(huán)引用(circular references)和加速重復(fù)嵌套查詢。 默認(rèn)值為 SESSION痰滋,這種情況下會(huì)緩存一個(gè)會(huì)話中執(zhí)行的所有查詢摘能。 若設(shè)置值為 STATEMENT,本地會(huì)話僅用在語(yǔ)句執(zhí)行上敲街,對(duì)相同 SqlSession 的不同調(diào)用將不會(huì)共享數(shù)據(jù)团搞。
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;

  // 當(dāng)沒(méi)有為參數(shù)提供特定的 JDBC 類型時(shí),為空值指定 JDBC 類型多艇。 某些驅(qū)動(dòng)需要指定列的 JDBC 類型逻恐,多數(shù)情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER梢莽。
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;

  // 指定對(duì)象的哪個(gè)方法觸發(fā)一次延遲加載萧豆。
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));

  // 設(shè)置超時(shí)時(shí)間,它決定驅(qū)動(dòng)等待數(shù)據(jù)庫(kù)響應(yīng)的秒數(shù)昏名。默認(rèn)不超時(shí)
  protected Integer defaultStatementTimeout;

  // 為驅(qū)動(dòng)的結(jié)果集設(shè)置默認(rèn)獲取數(shù)量涮雷。
  protected Integer defaultFetchSize;

  // SIMPLE 就是普通的執(zhí)行器;REUSE 執(zhí)行器會(huì)重用預(yù)處理語(yǔ)句(prepared statements)轻局; BATCH 執(zhí)行器將重用語(yǔ)句并執(zhí)行批量更新洪鸭。
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

  // 指定 MyBatis 應(yīng)如何自動(dòng)映射列到字段或?qū)傩浴?NONE 表示取消自動(dòng)映射;PARTIAL 只會(huì)自動(dòng)映射沒(méi)有定義嵌套結(jié)果集映射的結(jié)果集仑扑。 FULL 會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果集(無(wú)論是否嵌套)览爵。
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;

  // 指定發(fā)現(xiàn)自動(dòng)映射目標(biāo)未知列(或者未知屬性類型)的行為。這個(gè)值應(yīng)該設(shè)置為WARNING比較合適
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

  // settings下的properties屬性
  protected Properties variables = new Properties();

  // 默認(rèn)的反射器工廠,用于操作屬性镇饮、構(gòu)造器方便
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

  // 對(duì)象工廠, 所有的類resultMap類都需要依賴于對(duì)象工廠來(lái)實(shí)例化
  protected ObjectFactory objectFactory = new DefaultObjectFactory();

  // 對(duì)象包裝器工廠,主要用來(lái)在創(chuàng)建非原生對(duì)象,比如增加了某些監(jiān)控或者特殊屬性的代理類
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

  // 延遲加載的全局開(kāi)關(guān)蜓竹。當(dāng)開(kāi)啟時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載储藐。特定關(guān)聯(lián)關(guān)系中可通過(guò)設(shè)置fetchType屬性來(lái)覆蓋該項(xiàng)的開(kāi)關(guān)狀態(tài)俱济。
  protected boolean lazyLoadingEnabled = false;

  // 指定 Mybatis 創(chuàng)建具有延遲加載能力的對(duì)象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  // MyBatis 可以根據(jù)不同的數(shù)據(jù)庫(kù)廠商執(zhí)行不同的語(yǔ)句钙勃,這種多廠商的支持是基于映射語(yǔ)句中的 databaseId 屬性蛛碌。
  protected String databaseId;

  /**
   * Configuration factory class.
   * Used to create Configuration for loading deserialized unread properties.
   * 指定一個(gè)提供Configuration實(shí)例的類. 這個(gè)被返回的Configuration實(shí)例是用來(lái)加載被反序列化對(duì)象的懶加載屬性值. 這個(gè)類必須包含一個(gè)簽名方法static Configuration getConfiguration(). (從 3.2.3 版本開(kāi)始)
   */
  protected Class<?> configurationFactory;

  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

  // mybatis插件列表
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

  // 類型注冊(cè)器, 用于在執(zhí)行sql語(yǔ)句的出入?yún)⒂成湟约癿ybatis-config文件里的各種配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">時(shí)使用簡(jiǎn)寫(xiě), 后面會(huì)詳細(xì)解釋
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<String>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

  /*
   * A map holds cache-ref relationship. The key is the namespace that
   * references a cache bound to another namespace and the value is the
   * namespace which the actual cache is bound to.
   */
  protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

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

  public Configuration() {
    // 內(nèi)置別名注冊(cè)
    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);
  }

  ... 省去不必要的getter/setter

  public Class<? extends VFS> getVfsImpl() {
    return this.vfsImpl;
  }

  public void setVfsImpl(Class<? extends VFS> vfsImpl) {
    if (vfsImpl != null) {
      this.vfsImpl = vfsImpl;
      VFS.addImplClass(this.vfsImpl);
    }
  }

  public ProxyFactory getProxyFactory() {
    return proxyFactory;
  }

  public void setProxyFactory(ProxyFactory proxyFactory) {
    if (proxyFactory == null) {
      proxyFactory = new JavassistProxyFactory();
    }
    this.proxyFactory = proxyFactory;
  }

  /**
   * @since 3.2.2
   */
  public List<Interceptor> getInterceptors() {
    return interceptorChain.getInterceptors();
  }

  public LanguageDriverRegistry getLanguageRegistry() {
    return languageRegistry;
  }

  public void setDefaultScriptingLanguage(Class<?> driver) {
    if (driver == null) {
      driver = XMLLanguageDriver.class;
    }
    getLanguageRegistry().setDefaultDriverClass(driver);
  }

  public LanguageDriver getDefaultScriptingLanguageInstance() {
    return languageRegistry.getDefaultDriver();
  }

  /** @deprecated Use {@link #getDefaultScriptingLanguageInstance()} */
  @Deprecated
  public LanguageDriver getDefaultScriptingLanuageInstance() {
    return getDefaultScriptingLanguageInstance();
  }

  public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
  }

  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

  public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
    keyGenerators.put(id, keyGenerator);
  }

  public Collection<String> getKeyGeneratorNames() {
    return keyGenerators.keySet();
  }

  public Collection<KeyGenerator> getKeyGenerators() {
    return keyGenerators.values();
  }

  public KeyGenerator getKeyGenerator(String id) {
    return keyGenerators.get(id);
  }

  public boolean hasKeyGenerator(String id) {
    return keyGenerators.containsKey(id);
  }

  public void addCache(Cache cache) {
    caches.put(cache.getId(), cache);
  }

  public Collection<String> getCacheNames() {
    return caches.keySet();
  }

  public Collection<Cache> getCaches() {
    return caches.values();
  }

  public Cache getCache(String id) {
    return caches.get(id);
  }

  public boolean hasCache(String id) {
    return caches.containsKey(id);
  }

  public void addResultMap(ResultMap rm) {
    resultMaps.put(rm.getId(), rm);
    checkLocallyForDiscriminatedNestedResultMaps(rm);
    checkGloballyForDiscriminatedNestedResultMaps(rm);
  }

  public Collection<String> getResultMapNames() {
    return resultMaps.keySet();
  }

  public Collection<ResultMap> getResultMaps() {
    return resultMaps.values();
  }

  public ResultMap getResultMap(String id) {
    return resultMaps.get(id);
  }

  public boolean hasResultMap(String id) {
    return resultMaps.containsKey(id);
  }

  public void addParameterMap(ParameterMap pm) {
    parameterMaps.put(pm.getId(), pm);
  }

  public Collection<String> getParameterMapNames() {
    return parameterMaps.keySet();
  }

  public Collection<ParameterMap> getParameterMaps() {
    return parameterMaps.values();
  }

  public ParameterMap getParameterMap(String id) {
    return parameterMaps.get(id);
  }

  public boolean hasParameterMap(String id) {
    return parameterMaps.containsKey(id);
  }

  public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }

  public Collection<String> getMappedStatementNames() {
    buildAllStatements();
    return mappedStatements.keySet();
  }

  public Collection<MappedStatement> getMappedStatements() {
    buildAllStatements();
    return mappedStatements.values();
  }

  public Collection<XMLStatementBuilder> getIncompleteStatements() {
    return incompleteStatements;
  }

  public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
    incompleteStatements.add(incompleteStatement);
  }

  public Collection<CacheRefResolver> getIncompleteCacheRefs() {
    return incompleteCacheRefs;
  }

  public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
    incompleteCacheRefs.add(incompleteCacheRef);
  }

  public Collection<ResultMapResolver> getIncompleteResultMaps() {
    return incompleteResultMaps;
  }

  public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
    incompleteResultMaps.add(resultMapResolver);
  }

  public void addIncompleteMethod(MethodResolver builder) {
    incompleteMethods.add(builder);
  }

  public Collection<MethodResolver> getIncompleteMethods() {
    return incompleteMethods;
  }

  public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }

  public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
    if (validateIncompleteStatements) {
      buildAllStatements();
    }
    return mappedStatements.get(id);
  }

  public Map<String, XNode> getSqlFragments() {
    return sqlFragments;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

  public void addMappers(String packageName, Class<?> superType) {
    mapperRegistry.addMappers(packageName, superType);
  }

  public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }

  public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

  public boolean hasMapper(Class<?> type) {
    return mapperRegistry.hasMapper(type);
  }

  public boolean hasStatement(String statementName) {
    return hasStatement(statementName, true);
  }

  public boolean hasStatement(String statementName, boolean validateIncompleteStatements) {
    if (validateIncompleteStatements) {
      buildAllStatements();
    }
    return mappedStatements.containsKey(statementName);
  }

  public void addCacheRef(String namespace, String referencedNamespace) {
    cacheRefMap.put(namespace, referencedNamespace);
  }

  /*
   * Parses all the unprocessed statement nodes in the cache. It is recommended
   * to call this method once all the mappers are added as it provides fail-fast
   * statement validation.
   */
  protected void buildAllStatements() {
    if (!incompleteResultMaps.isEmpty()) {
      synchronized (incompleteResultMaps) {
        // This always throws a BuilderException.
        incompleteResultMaps.iterator().next().resolve();
      }
    }
    if (!incompleteCacheRefs.isEmpty()) {
      synchronized (incompleteCacheRefs) {
        // This always throws a BuilderException.
        incompleteCacheRefs.iterator().next().resolveCacheRef();
      }
    }
    if (!incompleteStatements.isEmpty()) {
      synchronized (incompleteStatements) {
        // This always throws a BuilderException.
        incompleteStatements.iterator().next().parseStatementNode();
      }
    }
    if (!incompleteMethods.isEmpty()) {
      synchronized (incompleteMethods) {
        // This always throws a BuilderException.
        incompleteMethods.iterator().next().resolve();
      }
    }
  }

  /*
   * Extracts namespace from fully qualified statement id.
   *
   * @param statementId
   * @return namespace or null when id does not contain period.
   */
  protected String extractNamespace(String statementId) {
    int lastPeriod = statementId.lastIndexOf('.');
    return lastPeriod > 0 ? statementId.substring(0, lastPeriod) : null;
  }

  // Slow but a one time cost. A better solution is welcome.
  protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
    if (rm.hasNestedResultMaps()) {
      for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
        Object value = entry.getValue();
        if (value instanceof ResultMap) {
          ResultMap entryResultMap = (ResultMap) value;
          if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
            Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
            if (discriminatedResultMapNames.contains(rm.getId())) {
              entryResultMap.forceNestedResultMaps();
            }
          }
        }
      }
    }
  }

  // Slow but a one time cost. A better solution is welcome.
  protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
    if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
      for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
        String discriminatedResultMapName = entry.getValue();
        if (hasResultMap(discriminatedResultMapName)) {
          ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
          if (discriminatedResultMap.hasNestedResultMaps()) {
            rm.forceNestedResultMaps();
            break;
          }
        }
      }
    }
  }

  protected static class StrictMap<V> extends HashMap<String, V> {

    private static final long serialVersionUID = -4950446264854982944L;
    private final String name;

    public StrictMap(String name, int initialCapacity, float loadFactor) {
      super(initialCapacity, loadFactor);
      this.name = name;
    }

    public StrictMap(String name, int initialCapacity) {
      super(initialCapacity);
      this.name = name;
    }

    public StrictMap(String name) {
      super();
      this.name = name;
    }

    public StrictMap(String name, Map<String, ? extends V> m) {
      super(m);
      this.name = name;
    }

    @SuppressWarnings("unchecked")
    public V put(String key, V value) {
      if (containsKey(key)) {
        throw new IllegalArgumentException(name + " already contains value for " + key);
      }
      if (key.contains(".")) {
        final String shortKey = getShortName(key);
        if (super.get(shortKey) == null) {
          super.put(shortKey, value);
        } else {
          super.put(shortKey, (V) new Ambiguity(shortKey));
        }
      }
      return super.put(key, value);
    }

    public V get(Object key) {
      V value = super.get(key);
      if (value == null) {
        throw new IllegalArgumentException(name + " does not contain value for " + key);
      }
      if (value instanceof Ambiguity) {
        throw new IllegalArgumentException(((Ambiguity) value).getSubject() + " is ambiguous in " + name
            + " (try using the full name including the namespace, or rename one of the entries)");
      }
      return value;
    }

    private String getShortName(String key) {
      final String[] keyParts = key.split("\\.");
      return keyParts[keyParts.length - 1];
    }

    protected static class Ambiguity {
      final private String subject;

      public Ambiguity(String subject) {
        this.subject = subject;
      }

      public String getSubject() {
        return subject;
      }
    }
  }
}

mybatis中所有環(huán)境配置、resultMap集合辖源、sql語(yǔ)句集合蔚携、插件列表、緩存克饶、加載的xml列表酝蜒、類型別名、類型處理器等全部都維護(hù)在Configuration中彤路。Configuration中包含了一個(gè)內(nèi)部靜態(tài)類StrictMap秕硝,它繼承于HashMap,對(duì)HashMap的裝飾在于增加了put時(shí)防重復(fù)的處理洲尊,get時(shí)取不到值時(shí)候的異常處理,這樣核心應(yīng)用層就不需要額外關(guān)心各種對(duì)象異常處理,簡(jiǎn)化應(yīng)用層邏輯奈偏。
  從設(shè)計(jì)上來(lái)說(shuō)坞嘀,我們可以說(shuō)Configuration并不是一個(gè)thin類(也就是僅包含了屬性以及getter/setter),而是一個(gè)rich類惊来,它對(duì)部分邏輯進(jìn)行了封裝便于用戶直接使用,而不是讓用戶各自散落處理丽涩,比如addResultMap方法和getMappedStatement方法等等。

最新的完整mybatis每個(gè)配置屬性含義可參考http://www.mybatis.org/mybatis-3/zh/configuration.html。
  從Configuration構(gòu)造器和protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();可以看出矢渊,所有我們?cè)趍ybatis-config和mapper文件中使用的類似int/string/JDBC/POOLED等字面常量最終解析為具體的java類型都是在typeAliasRegistry構(gòu)造器和Configuration構(gòu)造器執(zhí)行期間初始化的继准。下面我們來(lái)每塊分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市矮男,隨后出現(xiàn)的幾起案子移必,更是在濱河造成了極大的恐慌,老刑警劉巖毡鉴,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崔泵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡猪瞬,警方通過(guò)查閱死者的電腦和手機(jī)憎瘸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)陈瘦,“玉大人幌甘,你說(shuō)我怎么就攤上這事∪睿” “怎么了锅风?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)线婚。 經(jīng)常有香客問(wèn)我遏弱,道長(zhǎng),這世上最難降的妖魔是什么塞弊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任漱逸,我火速辦了婚禮,結(jié)果婚禮上游沿,老公的妹妹穿的比我還像新娘饰抒。我一直安慰自己,他們只是感情好诀黍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布袋坑。 她就那樣靜靜地躺著,像睡著了一般眯勾。 火紅的嫁衣襯著肌膚如雪枣宫。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,929評(píng)論 1 290
  • 那天吃环,我揣著相機(jī)與錄音也颤,去河邊找鬼。 笑死郁轻,一個(gè)胖子當(dāng)著我的面吹牛翅娶,可吹牛的內(nèi)容都是我干的文留。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼竭沫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燥翅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蜕提,我...
    開(kāi)封第一講書(shū)人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤森书,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后贯溅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拄氯,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年它浅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了译柏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姐霍,死狀恐怖鄙麦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情镊折,我是刑警寧澤胯府,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站恨胚,受9級(jí)特大地震影響骂因,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赃泡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一寒波、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧升熊,春花似錦俄烁、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蓖柔,卻和暖如春辰企,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背况鸣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工蟆豫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人懒闷。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親愤估。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帮辟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容