MyBatis如何解析mapper.xml中的sql相关信息(二)

首先附上一张简单的MyBatis的配置文件xml图,便于后续查看

}%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节点下的所有内容,那么继续看下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节点内容,可以看到有关解析配置文件里的一些节点方法,有plugins,settings,environments的,还有mappers就是包含我们所有mapper.xml标签(单独的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下的子节点mapper.xml,getChildren()方法返回的是List<XNode>所以可以调用iterator()来遍历。

得到了 XNode child = (XNode)i$.next(); 子节点之后,根据child.getName(),返回当前节点名并且判断是否等于package,显然不等于(节点名是mapper),随后开始在执行else下的代码

resource = child.getStringAttribute("resource");

通过getStringAttribute("resource")方法拿到遍历的每个mapper节点的属性名为"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文件,不多说,很稳!
那还是进到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节点下的所有内容,是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()方法里代码写了,规定了namespace不能为空
其他的解析方法都能看懂,这里直接看下面的context.evalNodes("select|insert|update|delete")这行代码,
这里就是返回所有select或ins,或up,或的节点内容,返回值依旧是List<XNode>,现在进入到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()为空,继续执行他的重载方法

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

    }

然后看到依然是,初始化,在调用初始化类的方法,发现整个程序的逻辑,以及代码的编写,基本都是这个样子的,在看下通过初始化的类调用的他的parseStatementNode()的这个方法。

到现在,第二篇,发现了从一开始的返回整个Configuration后,再次获得Mappers的内容,然后遍历Mappers中的mapper,
再次解析每一个mapper.xml。 在每个mapper标签中判断是否有namespace属性,继续解析xml方式得到select,insert,update,delete的标签节点,得到List<XNode>,最后遍历List<XNode>中的每一个的元素以及内容,这里就到了如何具体的解析具体的sql 标签了。下一篇继续在看吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容