MyBatis源码解析(十一)——Parsing解析模块的通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)

原创作品,可以转载,但是要标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html

1、回顾

  上面的几篇解析了型模块,在MyBatis中列模块包含的虽是Java类型与Jdbc类型,和其中的更换处理。类型模块于整个MyBatis功能架构中属于基础零部件有,是提前注册及注册器中,并安排到Configuration中备用。

  从马上同首开始解析Parsing解析模块,这个模块不同为Type模块,这个模块更如是平等拟工具模块。本篇先解析通用标记解析器GenericTokenParser。

2、通用标记解析器

  这里的通用标记解析器处理的凡SQL脚论被#{parameter}、${parameter}参数,根据被定TokenHandler(标记处理器)来开展处理,TokenHandler是记真正的微处理器,而本篇的解析器只是电脑处理的前提工序——解析,本类重在解析,而不处理,具体的处理会调用具体的TokenHandler的handleToken()方法来就。

  下面来看看该类的源码,首先就是是字段与构造器:

 1   //有一个开始和结束记号
 2   private final String openToken;
 3   private final String closeToken;
 4   //记号处理器
 5   private final TokenHandler handler;
 6 
 7   public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
 8     this.openToken = openToken;
 9     this.closeToken = closeToken;
10     this.handler = handler;
11   }

  于此类中定义了三独字段,分别吗openToken(开始标记)、closeToken(结束标记)、handler(标记处理器),而且经过带参数的构造器进行赋值,且只有如此一个构造器,如果要调用该解析器,必然要也夫三单参数进行赋值来创造其实例来完成解析工作。

  本类的主要就以底下的此分析方法parse()方法及:

 1   public String parse(String text) {
 2     StringBuilder builder = new StringBuilder();
 3     if (text != null && text.length() > 0) {
 4       char[] src = text.toCharArray();
 5       int offset = 0;
 6       int start = text.indexOf(openToken, offset);
 7       //#{favouriteSection,jdbcType=VARCHAR}
 8       //这里是循环解析参数,参考GenericTokenParserTest,比如可以解析${first_name} ${initial} ${last_name} reporting.这样的字符串,里面有3个 ${}
 9       while (start > -1) {
10           //判断一下 ${ 前面是否是反斜杠,这个逻辑在老版的mybatis中(如3.1.0)是没有的
11         if (start > 0 && src[start - 1] == '\\') {
12           // the variable is escaped. remove the backslash.
13             //新版已经没有调用substring了,改为调用如下的offset方式,提高了效率
14           //issue #760
15           builder.append(src, offset, start - offset - 1).append(openToken);
16           offset = start + openToken.length();
17         } else {
18           int end = text.indexOf(closeToken, start);
19           if (end == -1) {
20             builder.append(src, offset, src.length - offset);
21             offset = src.length;
22           } else {
23             builder.append(src, offset, start - offset);
24             offset = start + openToken.length();
25             String content = new String(src, offset, end - offset);
26             //得到一对大括号里的字符串后,调用handler.handleToken,比如替换变量这种功能
27             builder.append(handler.handleToken(content));
28             offset = end + closeToken.length();
29           }
30         }
31         start = text.indexOf(openToken, offset);
32       }
33       if (offset < src.length) {
34         builder.append(src, offset, src.length - offset);
35       }
36     }
37     return builder.toString();
38   }

  这里要使强调一下分析的意,解析就是解读,就是一旦明白对象是呀,但是未会见针对目标进行其他处理,解析的目的在认知,而不处理。那么如此来拘禁这方法的目的呢是如此,MyBatis将分析和拍卖分开布置,只以最终经调用标记处理器中的拍卖方法来完成标记处理工作,其余的代码一律都是分析认知,当然计算机就算解析出为不见面认识那是呀东西,所以这边的辨析更像的游说即使是将同样丰富串字符串中的有的拿走到的表示。然后对获取之部分子串进行响应的拍卖。

  第一步:该法的参数text其实一般是SQL脚论字符串,首先验证参数是否也null,如果为null,直接返回一个空字符串;如果非也null,则履行下同样步处理。

  第二步:将给定字符串转为字符数组src,并定义偏移量offset为0,然后拿走openToken子串在text中的首先破出现的初步下标start,执行下一致步。

  第三步:验证start是否高于-1(亦即被一定参数text中存在openToken子串),如果过量-1(开启循环),验证在让定text的start位置的前方一样位字符是否也”\”(反斜扛),如果是倒转斜杠,说明获取到的参数为挡了,我们得去这个倒斜杠,并再度定位offset。当然如果非是倒转斜扛,说明参数正常,则履行第四步。

  第四步:获取第一独门当户对配子串的末位位置end,如果end为-1,表示不有closeToken,则获末位end之前的富有串,并重复定位offset为src数组长度,如果end值不是-1,说明text字符串中留存了标记closeToken,则执行下一致步

  第五步:首先获得开始标记之前的子串,并更定位偏移量offset(start+开始标记的长=具体参数开始位置),获取这参数串为content,然后调用TokenHandler的handleToken()方法对得到的参数串进行处理(比如替换参数之类),然后拿拍卖后底错上加到之前的子串之上,再次重新定位偏移量offset为了却标记的下一样各类(end+closeToken的尺寸=end+1)。

  第六步:获取text中产一样步openToken的发端位置,重置start,执行循环体第三步到第六步,处理各一个参数,直到最终一个参数,循环结束,执行第七步。

  第七步:最后验明正身偏移量offset与src数组的长短,如果offset小,说明原串还有一些不补加到新串之上,将最终剩余部分添加到新串,然后拿新串返回,如果offset不小于src的数组长度,则一直返回新串

  总结:这个法的用意就是经参数的开标记和结束标记,循环获取SQL串中的参数,并针对性该展开自然的拍卖,组合成新串下,架构新串返回。就是这般简单!

  它的分析就是收获SQL脚论串中的参数子串。

3、标记处理器:TokenHandler

  这是一个接口,用于定义标记处理器,当我们只要兑现一个切实可行的号处理器时,直接实现此接口即可,可以望那源码,极其简约:

1 package org.apache.ibatis.parsing;
2 /**
3  * 记号处理器
4  * 
5  */
6 public interface TokenHandler {
7     //处理记号
8   String handleToken(String content);
9 }

  接口中只有表明了一个handleToken()方法,这是处理标记的点子。在MyBatis中实际现类大多为中类的法门展现,有藏身名叫中类、静态内部类等。

  在org.apache.ibatis.parsing确保下不只有定义了大概的符处理器接口,还有一个特性解析器PropertyParser,这多亏一个尊重的解析器实现,下对这近乎进行分析:

 1 package org.apache.ibatis.parsing;
 2 import java.util.Properties;
 3 
 4 /**
 5  * 属性解析器
 6  */
 7 public class PropertyParser {
 8 
 9   private PropertyParser() {
10     // Prevent Instantiation
11   }
12 
13   public static String parse(String string, Properties variables) {
14     VariableTokenHandler handler = new VariableTokenHandler(variables);
15     GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
16     return parser.parse(string);
17   }
18 
19   //就是一个map,用相应的value替换key
20   private static class VariableTokenHandler implements TokenHandler {
21     private Properties variables;
22 
23     public VariableTokenHandler(Properties variables) {
24       this.variables = variables;
25     }
26 
27     @Override
28     public String handleToken(String content) {
29       if (variables != null && variables.containsKey(content)) {
30         return variables.getProperty(content);
31       }
32       return "${" + content + "}";
33     }
34   }
35 }

  这里开始就是定义了一个私房的无参构造器,目的何?禁止实例化,不错,这个解析器是当做一个家伙类设在,用于属性解析处理,其分析方法是静态的,可以一直类名点用,虽然为堪采取实例调用,但以这里通过将构造器私有化的行明令禁止了这种办法,这样啊就减少了项目遭到实例的数,不见面每次调用都见面新建实例而招致大量实例堆积。

  再看parser()方法,首先创建了一个符号处理器的实例,这个符号处理器是PropertyParser的一个静态内部类,这个仿佛实现了TokenHandler接口,用于落实属性解析工作,所谓的性解析,就是通过加的key在Properties属性列表中查询得到相应之value进行替换。而现实的辨析工作则到由GenericTokenParser来负责。

  这个是MyBatis中一个TokenHandler的经典实用范例。

 

网站地图xml地图