31、propertise解析之通用标记解析器与标记处理器

来源:互联网 发布:网络微营销 编辑:程序博客网 时间:2024/05/17 22:45

GenericTokenParser通用标记解析器

这里的通用标记解析器处理的是SQL脚本中#{parameter}、${parameter}参数,根据给定TokenHandler(标记处理器)来进行处理,TokenHandler是标记真正的处理器,而本篇的解析器只是处理器处理的前提工序——解析,本类重在解析,而非处理,具体的处理会调用具体的TokenHandler的handleToken()方法来完成。

package org.apache.ibatis.parsing;/** * @author Clinton Begin */public class GenericTokenParser {  private final String openToken;//这个比如#{ 或者${  private final String closeToken;//这里基本上就是}  private final TokenHandler handler;//根据#{key}或者${key}得到key的值  public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {    this.openToken = openToken;    this.closeToken = closeToken;    this.handler = handler;  }    //"select * form table where id=#{name}"    /**     * 这个就是替换的了${key} 然后通过hanlder获取value 拼接进去     * @param text     * @return     */  public String parse(String text) {    if (text == null || text.isEmpty()) {      return "";    }    char[] src = text.toCharArray();    int offset = 0;    // search open token    int start = text.indexOf(openToken, offset);    if (start == -1) {//没有使用类似#{}的参数      return text;    }    final StringBuilder builder = new StringBuilder();    StringBuilder expression = null;    while (start > -1) {        //判断一下 ${ 前面是否是反斜杠,这个逻辑在老版的mybatis中(如3.1.0)是没有的      if (start > 0 && src[start - 1] == '\\') {//        // this open token is escaped. remove the backslash and continue.        builder.append(src, offset, start - offset - 1).append(openToken);        offset = start + openToken.length();      } else {        // found open token. let's search close token.        if (expression == null) {          expression = new StringBuilder();        } else {          expression.setLength(0);        }        builder.append(src, offset, start - offset);        offset = start + openToken.length();        int end = text.indexOf(closeToken, offset);        while (end > -1) {          if (end > offset && src[end - 1] == '\\') {            // this close token is escaped. remove the backslash and continue.            expression.append(src, offset, end - offset - 1).append(closeToken);            offset = end + closeToken.length();            end = text.indexOf(closeToken, offset);          } else {            expression.append(src, offset, end - offset);            offset = end + closeToken.length();            break;          }        }        if (end == -1) {          // close token was not found.          builder.append(src, start, src.length - start);          offset = src.length;        } else {            //得到一对大括号里的字符串后,调用handler.handleToken,比如替换变量这种功能          builder.append(handler.handleToken(expression.toString()));          offset = end + closeToken.length();        }      }      start = text.indexOf(openToken, offset);    }    if (offset < src.length) {      builder.append(src, offset, src.length - offset);    }    return builder.toString();  }}
  • 该方法的参数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的数组长度,则直接返回新串

TokenHandler接口

标记处理器,根据#{key}或者${key}得到key的值

package org.apache.ibatis.parsing;/** * 记号处理器 *  */public interface TokenHandler {    //处理记号  String handleToken(String content);}

接口中只申明了一个handleToken()方法,这是处理标记的方法。在MyBatis中其实现类大多以内部类的方式呈现,有匿名内部类、静态内部类等。

PropertyParser 属性解析器

在org.apache.ibatis.parsing包下不只定义了简单的标记处理器接口,还有一个属性解析器PropertyParser,这正是一个纯正的解析器实现,下面对这个类进行解析:
解析${}的使用
这个类主要是用来解析引入的property文件

package org.apache.ibatis.parsing;import java.util.Properties;/** * @author Clinton Begin * @author Kazuki Shimizu */public class PropertyParser {  private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";  public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";  public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";  private static final String ENABLE_DEFAULT_VALUE = "false";  private static final String DEFAULT_VALUE_SEPARATOR = ":";  private PropertyParser() {    // Prevent Instantiation  }  public static String parse(String string, Properties variables) {    VariableTokenHandler handler = new VariableTokenHandler(variables);    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);    return parser.parse(string);  }    //就是一个map,用相应的value替换key  private static class VariableTokenHandler implements TokenHandler {    private final Properties variables;    private final boolean enableDefaultValue;    private final String defaultValueSeparator;    private VariableTokenHandler(Properties variables) {      this.variables = variables;      //是否是使用默认的分隔符,在properties文件中配置      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));      //获取分隔符      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);    }      /**       * 配置类中是否配置了默认的分隔符,       * 开启默认的key: org.apache.ibatis.parsing.PropertyParser.default-value-separator value:true       * @param key       * @param defaultValue       * @return       */    private String getPropertyValue(String key, String defaultValue) {      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);    }    @Override    public String handleToken(String content) {      if (variables != null) {        String key = content;        if (enableDefaultValue) {//property文件中没有配置          final int separatorIndex = content.indexOf(defaultValueSeparator);//指定的字符在字符串中第一次出现的位置          String defaultValue = null;          if (separatorIndex >= 0) {            key = content.substring(0, separatorIndex);            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());          }          if (defaultValue != null) {            return variables.getProperty(key, defaultValue);          }        }        if (variables.containsKey(key)) {          return variables.getProperty(key);        }      }      return "${" + content + "}";    }  }}

这里开头就定义了一个私有的无参构造器,目的何在?禁止实例化,不错,这个解析器是作为一个工具类而存在,用于属性解析处理,其解析方法是静态的,可以直接类名点用,虽然也可以使用实例调用,但在这里通过将构造器私有化的行为明令禁止了这种方式,这样也就减少了项目中实例的数量,不会每次调用都会新建实例而导致大量实例堆积。

再看parser()方法,首先创建了一个标记处理器的实例,这个标记处理器是PropertyParser的一个静态内部类,这个类实现了TokenHandler接口,用于实现属性解析工作,所谓的属性解析,就是通过给定的key在Properties属性列表中查询获取对应的value进行替换。而具体的解析工作则交由GenericTokenParser来负责

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为应用锁密码忘了怎么办 华为麦芒5密码忘了怎么办 华为卡1无服务怎么办 经常卡1无服务怎么办 华为手机进水无服务怎么办 苹果手机进水后无服务怎么办 苹果6进水无服务怎么办 华为手机突然无服务怎么办 sim卡显示无服务怎么办 华为麦芒进水无限开关机怎么办 华为麦芒5进水黑屏怎么办 华为麦芒6进水了怎么办 4g手机开不开机怎么办 全屏钢化膜总是翘边怎么办 华为麦芒屏幕触屏失灵怎么办 华为麦芒5运行慢怎么办 手机屏保密码忘记了怎么办 麦芒5密码锁忘了怎么办 超薄手机壳松了怎么办 华为麦芒5声音小怎么办 笔记本外壳a面裂了怎么办 苹果手机外壳摔坏了怎么办 挂衣服肩膀出包怎么办 摩拜单车手机号注销了怎么办 摩拜单车手机号码换了怎么办 摩拜单车换手机号码打不开怎么办 摩拜单车丢了怎么办 摩拜单车忘锁了怎么办 透明手机壳粘指纹怎么办 tpu手机壳变黄了怎么办 0pp0手机声音小怎么办 橡胶皮套晒坏了怎么办 宝宝晚上吹空调发烧怎么办 玩手机手指尖疼怎么办 手机型号不支持微信运动怎么办 手腕向下压会疼怎么办 手腕韧带拉伤怎么办恢复快 华为手机用车载充电慢怎么办 华为麦芒6充电慢怎么办 oppo手机压弯了怎么办 麦芒5电池不耐用怎么办