mybatis解析XML我爱编程

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

2017-08-07  本文已影响29人  萤火之森ss

首先附上一张简单的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 标签了。下一篇继续在看吧。

上一篇下一篇

猜你喜欢

热点阅读