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来负责
- 31、propertise解析之通用标记解析器与标记处理器
- PHP 标记解析
- VML标记与通用属性
- VML标记与通用属性
- JSP自定义标记 标记处理器(TH) 构造器及生命周期
- 开发标记—JS之Json数据解析
- 定制标记处理器
- div标记与span标记
- SSR标记与SNP标记
- 标记
- 标记
- 标记
- 标记
- 标记
- 标记
- 标记
- 标记
- 标记
- SVN安装、MyEclipse安装SVN插件以及上传项目
- mysql安装及连接
- ES6规范大全
- 文章标题
- Angular2 父子路由问题
- 31、propertise解析之通用标记解析器与标记处理器
- uml关联符号说明
- 32、解析MetaObject
- Android基础--ListView的显示几种方式
- 1001. 求高精度幂
- 快速Android开发系列网络篇之Android-Async-Http
- 新的启程,新的进步。
- 33、SQL解析基础
- 《操作系统真象还原》-阅读笔记(中)