美文网首页mybatis解析XML我爱编程
MyBatis如何解析mapper.xml中的sql相关信息(二

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

作者: 萤火之森ss | 来源:发表于2017-08-07 10:33 被阅读29次

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

    相关文章

      网友评论

        本文标题:MyBatis如何解析mapper.xml中的sql相关信息(二

        本文链接:https://www.haomeiwen.com/subject/epuolxtx.html