【Mybatis源码】GenericTokenParser解析器

13 篇文章 0 订阅 ¥69.90 ¥99.00
本文详细介绍了Mybatis中的GenericTokenParser解析器的工作原理,包括构造方法、parse方法的解析过程,以及如何使用它进行文本解析。同时,文章还探讨了PropertyParser解析器,讲解了它的静态方法、VariableTokenHandler类的功能,以及如何在实际测试中应用PropertyParser。
摘要由CSDN通过智能技术生成

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

一、GenericTokenParser构造方法

public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
  this.openToken = openToken;
  this.closeToken = closeToken;
  this.handler = handler;
}

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

TokenHandler接口:

public interface TokenHandler {
  String handleToken(String content);
}

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

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

二、parse方法

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

public String parse(String text) {
  if (text == null || text.isEmpty()) {
    return "";
  }
  // search open token
  int start = text.indexOf(openToken, 0);
  if (start == -1) {
    return text;
  }
  char[] src = text.toCharArray();
  int offset = 0;
  final StringBuilder builder = new StringBuilder();
  StringBuilder expression = null;
  while (start > -1) {
    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 {
        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();
}

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

三、GenericTokenParser的使用

1、引入依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>

2、TokenHandler实现

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

package cn.horse.demo.parser;

import org.apache.ibatis.parsing.TokenHandler;

import java.util.Properties;

public class VariableTokenHandler implements TokenHandler {

    private final Properties variables;

    public VariableTokenHandler(Properties variables) {
        this.variables = variables;
    }

    @Override
    public String handleToken(String content) {
        return variables.getProperty(content);
    }
}

3、解析文本

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

package cn.horse.demo.parser;

import org.apache.ibatis.parsing.GenericTokenParser;
import org.junit.Assert;
import org.junit.Test;

import java.util.Properties;

public class GenericTokenParserTest {

    @Test
    public void testParse() {
        Properties variables = new Properties();
        variables.put("id", "1");
        variables.put("name", "张三");
        variables.put("age", "20");
        GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(variables));
        Assert.assertEquals("${id}: 1", parser.parse("\\${id}: ${id}"));
        Assert.assertEquals("${name}: 张三", parser.parse("\\${name}: ${name}"));
        Assert.assertEquals("${age}: 20", parser.parse("\\${age}: ${age}"));
    }
}

执行单元测试通过

四、PropertyParser解析器

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

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

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

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 static class VariableTokenHandler implements TokenHandler {
    private final Properties variables;
    private final boolean enableDefaultValue;
    private final String defaultValueSeparator;

    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    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) {
          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 + "}";
    }
  }

}

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、引入依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>

2、解析文本

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

package cn.horse.demo.parser;

import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.parsing.PropertyParser;
import org.junit.Assert;
import org.junit.Test;

import java.util.Properties;

public class PropertyParserTest {

    @Test
    public void testParse() {
        Properties variables = new Properties();
        variables.put("id", "1");
        variables.put("name", "张三");
        variables.put("age", "20");
        Assert.assertEquals("${id}: 1", PropertyParser.parse("\\${id}: ${id}", variables));
        Assert.assertEquals("${name}: 张三", PropertyParser.parse("\\${name}: ${name}", variables));
        Assert.assertEquals("${age}: 20", PropertyParser.parse("\\${age}: ${age}", variables));
    }

    @Test
    public void testParseWithDefaultValue() {
        Properties variables = new Properties();
        variables.put("org.apache.ibatis.parsing.PropertyParser.enable-default-value", "true");
        variables.put("id", "1");
        variables.put("name", "张三");
        Assert.assertEquals("${id}: 1", PropertyParser.parse("\\${id}: ${id}", variables));
        Assert.assertEquals("${name}: 张三", PropertyParser.parse("\\${name}: ${name}", variables));
        Assert.assertEquals("${age}: 20", PropertyParser.parse("\\${age}: ${age:20}", variables));
    }
}

执行单元测试通过

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值