MyBatis如何解析mapper.xml中的sql相關(guān)信息(二)

首先附上一張簡單的MyBatis的配置文件xml圖,便于后續(xù)查看

}%P73TT)YL}O62{22YF48~S.png

在上篇中定位到源代碼XMLConfigBuilder類中的parse()方法

public Configuration parse() {
        if(this.parsed) {
            throw new BuilderException("Each MapperConfigParser can only be used once.");
        } else {
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

上篇了解到(this.parser.evalNode("/configuration")返回了MyBatis配置文件中的configuration節(jié)點下的所有內(nèi)容,那么繼續(xù)看下parseConfiguration()方法

private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.settingsElement(root.evalNode("settings"));
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

在這個方法中接受傳來的XNode的congfiguration節(jié)點內(nèi)容刁品,可以看到有關(guān)解析配置文件里的一些節(jié)點方法,有plugins分冈,settings派哲,environments的,還有mappers就是包含我們所有mapper.xml標(biāo)簽(單獨的Mybatis志群,未整合spring),那進入到mapperElement方法中着绷,依舊是XMLCongfigBuilder類中的方法:

private void mapperElement(XNode parent) throws Exception {
        if(parent != null) {
            Iterator i$ = parent.getChildren().iterator();
            while(true) {
                while(i$.hasNext()) {
                    XNode child = (XNode)i$.next();
                    String resource;
                    if("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if(resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if(resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if(resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

那這個方法就越來越清楚了,首先判斷XNode不為空锌云,就是mappers,然后獲得XNode的getChildren().iterator();mappers下的子節(jié)點mapper.xml荠医,getChildren()方法返回的是List<XNode>所以可以調(diào)用iterator()來遍歷。

得到了 XNode child = (XNode)i$.next(); 子節(jié)點之后,根據(jù)child.getName()彬向,返回當(dāng)前節(jié)點名并且判斷是否等于package兼贡,顯然不等于(節(jié)點名是mapper),隨后開始在執(zhí)行else下的代碼

resource = child.getStringAttribute("resource");

通過getStringAttribute("resource")方法拿到遍歷的每個mapper節(jié)點的屬性名為"resource"的值,那就是這篇文章的上圖中
<mapper resource="/UserMapper.xml" />這里的xml路徑娃胆,下面的兩行代碼

String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");

通過配置文件看到我們暫時沒有用到遍希,所以走下面if判斷的第一個分支

if(resource != null && url == null && mapperClass == null)

第一行代碼先略過-_- ErrorContext.instance().resource(resource);
下面是很熟悉的代碼

inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();

這里呢依舊是之前那樣的步驟,和解析MyBatis配置文件一樣解析mapper.xml文件里烦,不多說凿蒜,很穩(wěn)!
那還是進到parse()方法看看

public void parse() {
        if(!this.configuration.isResourceLoaded(this.resource)) {
            this.configurationElement(this.parser.evalNode("/mapper"));
            this.configuration.addLoadedResource(this.resource);
            this.bindMapperForNamespace();
        }

        this.parsePendingResultMaps();
        this.parsePendingChacheRefs();
        this.parsePendingStatements();
    }

首先還是判斷文件是否被解析胁黑,如果解析了的話废封,先放一下這個點,先看未解析的步驟丧蘸。

首先依舊是this.parser.evalNode("/mapper")漂洋,返回mapper節(jié)點下的所有內(nèi)容,是XNode類型力喷,在通過configurationElement()方法解析

private void configurationElement(XNode context) {
        try {
            String namespace = context.getStringAttribute("namespace");
            this.builderAssistant.setCurrentNamespace(namespace);
            this.cacheRefElement(context.evalNode("cache-ref"));
            this.cacheElement(context.evalNode("cache"));
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            this.resultMapElements(context.evalNodes("/mapper/resultMap"));
            this.sqlElement(context.evalNodes("/mapper/sql"));
            this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception var3) {
            throw new RuntimeException("Error parsing Mapper XML. Cause: " + var3, var3);
        }
    }

看到方法里有獲得屬性getStringAttribute("")方法氮发,有常見的namespace,parameterMap等等比較重要的冗懦,
可以看下setCurrentNamespace()方法里代碼寫了爽冕,規(guī)定了namespace不能為空
其他的解析方法都能看懂,這里直接看下面的context.evalNodes("select|insert|update|delete")這行代碼披蕉,
這里就是返回所有select或ins,或up,或的節(jié)點內(nèi)容颈畸,返回值依舊是List<XNode>,現(xiàn)在進入到XMLMapperBuilder類的
buildStatementFromContext方法

private void buildStatementFromContext(List<XNode> list) {
        if(this.configuration.getDatabaseId() != null) {
            this.buildStatementFromContext(list, this.configuration.getDatabaseId());
        }
        this.buildStatementFromContext(list, (String)null);
    }

this.configuration.getDatabaseId()為空没讲,繼續(xù)執(zhí)行他的重載方法

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        Iterator i$ = list.iterator();
        while(i$.hasNext()) {
            XNode context = (XNode)i$.next();
            XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

            try {
                statementParser.parseStatementNode();
            } catch (IncompleteElementException var7) {
                this.configuration.addIncompleteStatement(statementParser);
            }
        }

    }

然后看到依然是眯娱,初始化,在調(diào)用初始化類的方法爬凑,發(fā)現(xiàn)整個程序的邏輯徙缴,以及代碼的編寫,基本都是這個樣子的嘁信,在看下通過初始化的類調(diào)用的他的parseStatementNode()的這個方法于样。

到現(xiàn)在,第二篇潘靖,發(fā)現(xiàn)了從一開始的返回整個Configuration后穿剖,再次獲得Mappers的內(nèi)容,然后遍歷Mappers中的mapper,
再次解析每一個mapper.xml卦溢。 在每個mapper標(biāo)簽中判斷是否有namespace屬性糊余,繼續(xù)解析xml方式得到select秀又,insert,update,delete的標(biāo)簽節(jié)點,得到List<XNode>,最后遍歷List<XNode>中的每一個的元素以及內(nèi)容贬芥,這里就到了如何具體的解析具體的sql 標(biāo)簽了吐辙。下一篇繼續(xù)在看吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蘸劈,一起剝皮案震驚了整個濱河市袱讹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昵时,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椒丧,死亡現(xiàn)場離奇詭異壹甥,居然都是意外死亡,警方通過查閱死者的電腦和手機壶熏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門句柠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棒假,你說我怎么就攤上這事溯职。” “怎么了帽哑?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵谜酒,是天一觀的道長。 經(jīng)常有香客問我妻枕,道長僻族,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任屡谐,我火速辦了婚禮述么,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘愕掏。我一直安慰自己度秘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布饵撑。 她就那樣靜靜地躺著剑梳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滑潘。 梳的紋絲不亂的頭發(fā)上阻荒,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音众羡,去河邊找鬼侨赡。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羊壹。 我是一名探鬼主播蓖宦,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼油猫!你這毒婦竟也來了稠茂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤情妖,失蹤者是張志新(化名)和其女友劉穎睬关,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡证,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡电爹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了料睛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丐箩。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恤煞,靈堂內(nèi)的尸體忽然破棺而出屎勘,到底是詐尸還是另有隱情,我是刑警寧澤居扒,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布概漱,位于F島的核電站,受9級特大地震影響喜喂,放射性物質(zhì)發(fā)生泄漏犀概。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一夜惭、第九天 我趴在偏房一處隱蔽的房頂上張望姻灶。 院中可真熱鬧,春花似錦诈茧、人聲如沸产喉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曾沈。三九已至,卻和暖如春鸥昏,著一層夾襖步出監(jiān)牢的瞬間塞俱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工吏垮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留障涯,地道東北人罐旗。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像唯蝶,于是被迫代替她去往敵國和親九秀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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