MyBatis印象閱讀之XML資源文件加載(上)

在之前一系列文章中,我們介紹了MyBatis關(guān)于反射相關(guān)的工具類(lèi),今天開(kāi)始回歸主線(xiàn)任務(wù),繼續(xù)分析主流程源碼。我們欠下的技術(shù)債先放一下:

  • Configuration
  • TypeAliasRegistry
  • TypeHandlerRegistry

大家還記得否舶替,在《MyBatis印象閱讀之起航篇》我們到了分析XMl資源文件的步驟。那么接下來(lái)我們開(kāi)始分析源碼杠园。

1.XMLConfigBuilder源碼分析


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

在分析這塊源碼顾瞪,我們可以參照官網(wǎng)的configuration下有哪些標(biāo)簽和作用,MyBatis官網(wǎng) XML配置

1.1 propertiesElement方法解析


  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      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);
      configuration.setVariables(defaults);
    }
  }

代碼不難理解抛蚁,我們從源碼中可以看到配置來(lái)源有三種陈醒,分別resource、url最后是直接配置瞧甩。如果屬性不止在一個(gè)地方進(jìn)行了配置钉跷,那么MyBatis將按照下面的順序加載:

在 properties 元素體內(nèi)指定的屬性首先被讀取。
然后根據(jù) properties 元素中的 resource 屬性讀取類(lèi)路徑下屬性文件或根據(jù) url 屬性指定的路徑讀取屬性文件肚逸,并覆蓋已讀取的同名屬性爷辙。
最后讀取作為方法參數(shù)傳遞的屬性,并覆蓋已讀取的同名屬性朦促。
因此膝晾,通過(guò)方法參數(shù)傳遞的屬性具有最高優(yōu)先級(jí),resource/url 屬性中指定的配置文件次之务冕,最低優(yōu)先級(jí)的是 properties 屬性中指定的屬性血当。

這里最需要關(guān)注的就是最后兩句代碼,我們會(huì)把解析好的值放入解析器和全局變量,供之后解析使用臊旭。

1.2 settingsAsProperties方法解析

在分析源碼之前落恼,我們先通過(guò)官網(wǎng)來(lái)了解有哪些屬性:


<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

接下來(lái)我們?cè)賮?lái)看源碼:

  private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    // localReflectorFactory = new DefaultReflectorFactory();
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

這個(gè)方法源碼邏輯也比較簡(jiǎn)單,去除配置的setting屬性离熏,通過(guò)MetaClass校驗(yàn)Configuration是否有對(duì)應(yīng)屬性的set方法领跛,如果沒(méi)有就直接報(bào)錯(cuò)。這一步主要還是用來(lái)保護(hù)屬性的正確性撤奸。

接下來(lái)是
loadCustomVfs(settings);
loadCustomLogImpl(settings);
這個(gè)我沒(méi)用過(guò)這些配置,不做分析喊括,看名字應(yīng)該是自定義Vfs和Log胧瓜。我們接下來(lái)繼續(xù)看typeAliasesElement方法。

1.3 typeAliasesElement源碼解析

我們還是先來(lái)看下官方介紹:

類(lèi)型別名是為 Java 類(lèi)型設(shè)置一個(gè)短的名字郑什。 它只和 XML 配置有關(guān)府喳,存在的意義僅在于用來(lái)減少類(lèi)完全限定名的冗余。

例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

繼續(xù)來(lái)看源碼:

  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

這個(gè)方法說(shuō)難也不難蘑拯,主要還是在于typeAliasRegistry我們還未做過(guò)分析钝满,但是如果只是感念理解的話(huà)也很容易,我們?cè)谶@里可以當(dāng)做是一個(gè)Map來(lái)存儲(chǔ)原屬性的別名申窘。

之后按照順序是pluginElement方法弯蚜,這是用來(lái)解析插件的。

1.4 pluginElement源碼解析

我們繼續(xù)來(lái)看官網(wǎng)怎么說(shuō):

MyBatis 允許你在已映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用剃法。默認(rèn)情況下碎捺,MyBatis 允許使用插件來(lái)攔截的方法調(diào)用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

我們可以明白,這個(gè)插件是用來(lái)做擴(kuò)展用的贷洲,主要是攔截調(diào)用收厨,而幾個(gè)攔截點(diǎn)也給我們列舉了出來(lái),我們目前還不是很明白這些是干嘛的优构,但是先有個(gè)印象诵叁,知道plugins是可以擴(kuò)展的,而且擴(kuò)展點(diǎn)只有4個(gè)钦椭。我們先來(lái)看解析源碼:


  private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

例如配置為:

<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

這款代碼要理解還是很簡(jiǎn)單的拧额,就是把配置轉(zhuǎn)化為Interceptor類(lèi),并添加到configuration中玉凯。我們繼續(xù)看objectFactoryElement代碼势腮。

1.5 objectFactoryElement源碼解析

我們繼續(xù)看官網(wǎng)說(shuō)明:

MyBatis 每次創(chuàng)建結(jié)果對(duì)象的新實(shí)例時(shí),它都會(huì)使用一個(gè)對(duì)象工廠(chǎng)(ObjectFactory)實(shí)例來(lái)完成漫仆。 默認(rèn)的對(duì)象工廠(chǎng)需要做的僅僅是實(shí)例化目標(biāo)類(lèi)捎拯,要么通過(guò)默認(rèn)構(gòu)造方法,要么在參數(shù)映射存在的時(shí)候通過(guò)參數(shù)構(gòu)造方法來(lái)實(shí)例化。 如果想覆蓋對(duì)象工廠(chǎng)的默認(rèn)行為署照,則可以通過(guò)創(chuàng)建自己的對(duì)象工廠(chǎng)來(lái)實(shí)現(xiàn)祸泪。

這個(gè)我們基本也不會(huì)去改動(dòng),所以我們簡(jiǎn)單看下源碼就行:

  private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties properties = context.getChildrenAsProperties();
      ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
      factory.setProperties(properties);
      configuration.setObjectFactory(factory);
    }
  }

2.今日總結(jié)

我們分析了關(guān)于XML資源的部分解析資源源碼建芙,下一章我們會(huì)繼續(xù)接下來(lái)的分析没隘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市禁荸,隨后出現(xiàn)的幾起案子右蒲,更是在濱河造成了極大的恐慌,老刑警劉巖赶熟,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑰妄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡映砖,警方通過(guò)查閱死者的電腦和手機(jī)间坐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邑退,“玉大人竹宋,你說(shuō)我怎么就攤上這事〉丶迹” “怎么了蜈七?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)莫矗。 經(jīng)常有香客問(wèn)我宪潮,道長(zhǎng),這世上最難降的妖魔是什么趣苏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任狡相,我火速辦了婚禮,結(jié)果婚禮上食磕,老公的妹妹穿的比我還像新娘尽棕。我一直安慰自己,他們只是感情好彬伦,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布滔悉。 她就那樣靜靜地躺著,像睡著了一般单绑。 火紅的嫁衣襯著肌膚如雪回官。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天搂橙,我揣著相機(jī)與錄音歉提,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛苔巨,可吹牛的內(nèi)容都是我干的版扩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侄泽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼礁芦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起悼尾,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柿扣,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后闺魏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體窄刘,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年舷胜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片活翩。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烹骨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出材泄,到底是詐尸還是另有隱情沮焕,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布拉宗,位于F島的核電站峦树,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旦事。R本人自食惡果不足惜魁巩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姐浮。 院中可真熱鬧谷遂,春花似錦、人聲如沸卖鲤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蛋逾。三九已至集晚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間区匣,已是汗流浹背偷拔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人条摸。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓悦污,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钉蒲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子切端,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359