• 【Mybatis源码】GenericTokenParser解析器


    GenericTokenParser是Mybatis中定义的进行解析文本中标志的类,本篇我们主要介绍GenericTokenParser解析文本中标志的原理。

    一、GenericTokenParser构造方法

    1. public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
    2. this.openToken = openToken;
    3. this.closeToken = closeToken;
    4. this.handler = handler;
    5. }

    此构造方法用于初始化openToken(开始标志)、closeToken(结束标志)、handler(标志处理器);

    TokenHandler接口:

    1. public interface TokenHandler {
    2. String handleToken(String content);
    3. }

    TokenHandler接口中包含了一个方法名称为handleToken的方法,此方法用于将传入的内容处理后并返回。

    例如:文本中需要解析的标志是${name},则开始标识是${,结束标识是}

    二、parse方法

    GenericTokenParser中只包含了一个parse的方法,此方法接收一个文本,返回的是解析后的文本。

    1. public String parse(String text) {
    2. if (text == null || text.isEmpty()) {
    3. return "";
    4. }
    5. // search open token
    6. int start = text.indexOf(openToken, 0);
    7. if (start == -1) {
    8. return text;
    9. }
    10. char[] src = text.toCharArray();
    11. int offset = 0;
    12. final StringBuilder builder = new StringBuilder();
    13. StringBuilder expression = null;
    14. while (start > -1) {
    15. if (start > 0 && src[start - 1] == '\\') {
    16. // this open token is escaped. remove the backslash and continue.
    17. builder.append(src, offset, start - offset - 1).append(openToken);
    18. offset = start + openToken.length();
    19. } else {
    20. // found open token. let's search close token.
    21. if (expression == null) {
    22. expression = new StringBuilder();
    23. } else {
    24. expression.setLength(0);
    25. }
    26. builder.append(src, offset, start - offset);
    27. offset = start + openToken.length();
    28. int end = text.indexOf(closeToken, offset);
    29. while (end > -1) {
    30. if (end > offset && src[end - 1] == '\\') {
    31. // this close token is escaped. remove the backslash and continue.
    32. expression.append(src, offset, end - offset - 1).append(closeToken);
    33. offset = end + closeToken.length();
    34. end = text.indexOf(closeToken, offset);
    35. } else {
    36. expression.append(src, offset, end - offset);
    37. offset = end + closeToken.length();
    38. break;
    39. }
    40. }
    41. if (end == -1) {
    42. // close token was not found.
    43. builder.append(src, start, src.length - start);
    44. offset = src.length;
    45. } else {
    46. builder.append(handler.handleToken(expression.toString()));
    47. offset = end + closeToken.length();
    48. }
    49. }
    50. start = text.indexOf(openToken, offset);
    51. }
    52. if (offset < src.length) {
    53. builder.append(src, offset, src.length - offset);
    54. }
    55. return builder.toString();
    56. }

    此方法使用开始标志、结束标志找到标志表达式,并使用标志处理器(TokenHandler)解析标志后进行拼装。方法中对转义字符进行了特殊处理,如果开始标志或结束标志前的字符属于转义字符,则继续寻找下一个开始标志或结束标志。

    三、GenericTokenParser的使用

    1、引入依赖

    1. <dependency>
    2. <groupId>org.mybatisgroupId>
    3. <artifactId>mybatisartifactId>
    4. <version>3.4.5version>
    5. dependency>
    6. <dependency>
    7. <groupId>junitgroupId>
    8. <artifactId>junitartifactId>
    9. <version>4.13.1version>
    10. <scope>testscope>
    11. dependency>

    2、TokenHandler实现

    在src/test/java下的cn.horse.demo.parser包下新建VariableTokenHandler类实现TokenHandler接口

    1. package cn.horse.demo.parser;
    2. import org.apache.ibatis.parsing.TokenHandler;
    3. import java.util.Properties;
    4. public class VariableTokenHandler implements TokenHandler {
    5. private final Properties variables;
    6. public VariableTokenHandler(Properties variables) {
    7. this.variables = variables;
    8. }
    9. @Override
    10. public String handleToken(String content) {
    11. return variables.getProperty(content);
    12. }
    13. }

    3、解析文本

    在src/test/java下的cn.horse.demo.parser包下新建GenericTokenParserTest测试类

    1. package cn.horse.demo.parser;
    2. import org.apache.ibatis.parsing.GenericTokenParser;
    3. import org.junit.Assert;
    4. import org.junit.Test;
    5. import java.util.Properties;
    6. public class GenericTokenParserTest {
    7. @Test
    8. public void testParse() {
    9. Properties variables = new Properties();
    10. variables.put("id", "1");
    11. variables.put("name", "张三");
    12. variables.put("age", "20");
    13. GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(variables));
    14. Assert.assertEquals("${id}: 1", parser.parse("\\${id}: ${id}"));
    15. Assert.assertEquals("${name}: 张三", parser.parse("\\${name}: ${name}"));
    16. Assert.assertEquals("${age}: 20", parser.parse("\\${age}: ${age}"));
    17. }
    18. }

    执行单元测试通过

    四、PropertyParser解析器

    PropertyParser类是一个工具类,其不能被实例化,但是提供了静态方法用于文本的解析;下面我们直接看其提供的静态方法:

    1. public static String parse(String string, Properties variables) {
    2. VariableTokenHandler handler = new VariableTokenHandler(variables);
    3. GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    4. return parser.parse(string);
    5. }

    方法中使用GenericTokenParser进行文本的解析,开始标志使用的${,结束标志使用的},文本中标志的解析使用的是VariableTokenHandler类,VariableTokenHandler类是PropertyParser的静态内部类,下面我们看一下VariableTokenHandler类。

    1. public class PropertyParser {
    2. private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
    3. public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
    4. public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
    5. private static final String ENABLE_DEFAULT_VALUE = "false";
    6. private static final String DEFAULT_VALUE_SEPARATOR = ":";
    7. private static class VariableTokenHandler implements TokenHandler {
    8. private final Properties variables;
    9. private final boolean enableDefaultValue;
    10. private final String defaultValueSeparator;
    11. private VariableTokenHandler(Properties variables) {
    12. this.variables = variables;
    13. this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
    14. this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    15. }
    16. private String getPropertyValue(String key, String defaultValue) {
    17. return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    18. }
    19. @Override
    20. public String handleToken(String content) {
    21. if (variables != null) {
    22. String key = content;
    23. if (enableDefaultValue) {
    24. final int separatorIndex = content.indexOf(defaultValueSeparator);
    25. String defaultValue = null;
    26. if (separatorIndex >= 0) {
    27. key = content.substring(0, separatorIndex);
    28. defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
    29. }
    30. if (defaultValue != null) {
    31. return variables.getProperty(key, defaultValue);
    32. }
    33. }
    34. if (variables.containsKey(key)) {
    35. return variables.getProperty(key);
    36. }
    37. }
    38. return "${" + content + "}";
    39. }
    40. }
    41. }

    VariableTokenHandler对象创建过程:

    1. 会初始化变量集(variables)
    2. 初始化是否可用默认值,从变量集中获取org.apache.ibatis.parsing.PropertyParser.enable-default-value属性对应的值,默认为false
    3. 初始化默认值分隔符,从变量集中获取org.apache.ibatis.parsing.PropertyParser.default-value-separator属性对应的值,默认为:

    标志解析过程:

    1. 变量集不为空时,可用默认值时则根据默认值分隔符获取到默认值,默认值不为空时,变量集中不存在属性时返回默认值,存在属性时返回属性值;
    2. 变量集不为空时,变量集中存在属性时,返回属性值
    3. 其余情况返回${标志}

    五、PropertyParser的使用

    1、引入依赖

    1. <dependency>
    2. <groupId>org.mybatisgroupId>
    3. <artifactId>mybatisartifactId>
    4. <version>3.4.5version>
    5. dependency>
    6. <dependency>
    7. <groupId>junitgroupId>
    8. <artifactId>junitartifactId>
    9. <version>4.13.1version>
    10. <scope>testscope>
    11. dependency>

    2、解析文本

    在src/test/java下的cn.horse.demo.parser包下新建PropertyParserTest测试类

    1. package cn.horse.demo.parser;
    2. import org.apache.ibatis.parsing.GenericTokenParser;
    3. import org.apache.ibatis.parsing.PropertyParser;
    4. import org.junit.Assert;
    5. import org.junit.Test;
    6. import java.util.Properties;
    7. public class PropertyParserTest {
    8. @Test
    9. public void testParse() {
    10. Properties variables = new Properties();
    11. variables.put("id", "1");
    12. variables.put("name", "张三");
    13. variables.put("age", "20");
    14. Assert.assertEquals("${id}: 1", PropertyParser.parse("\\${id}: ${id}", variables));
    15. Assert.assertEquals("${name}: 张三", PropertyParser.parse("\\${name}: ${name}", variables));
    16. Assert.assertEquals("${age}: 20", PropertyParser.parse("\\${age}: ${age}", variables));
    17. }
    18. @Test
    19. public void testParseWithDefaultValue() {
    20. Properties variables = new Properties();
    21. variables.put("org.apache.ibatis.parsing.PropertyParser.enable-default-value", "true");
    22. variables.put("id", "1");
    23. variables.put("name", "张三");
    24. Assert.assertEquals("${id}: 1", PropertyParser.parse("\\${id}: ${id}", variables));
    25. Assert.assertEquals("${name}: 张三", PropertyParser.parse("\\${name}: ${name}", variables));
    26. Assert.assertEquals("${age}: 20", PropertyParser.parse("\\${age}: ${age:20}", variables));
    27. }
    28. }

    执行单元测试通过

  • 相关阅读:
    二叉树OJ
    jmeter监听每秒点击数(Hits per Second)
    多元宇宙算法求解电力系统多目标优化问题(Matlab实现)【电气期刊论文复现与】
    Java输入输出、常见场景解决方案、文件夹操作
    电脑重置与重装系统的区别
    【华为云IaaS基础三件套之----计算ECS、网络EIP、存储EVS】
    SpringBoot bbs(3~4) 过度创作总结。
    【设计模式-2】策略模式 - 避免冗余的if-else判断
    Rockland蛋白质印迹试剂丨Rockland SDS-PAGE脱色液
    Flink(七)【输出算子(Sink)】
  • 原文地址:https://blog.csdn.net/m1729339749/article/details/133694369