【深入理解MyBatis】- 04Mybatis 从0开始实现Mybatis 占位符替换${}功能

Mybatis 占位符替换${}简介

在Mybatis的配置文件中,可以使用类型${username}这种占位符配置,将具体配置的属性单独统一的配置文件中,使用如下
mybatis-config.xml

<dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
</dataSource>

mybatis-config.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1/mybatis
username=root
password=root

由于占位符替换在实现工作中使用频率较高,如

  1. 短信模块: ${NAME}在${TIME},存钱${MONEY}。【中国人民银行】
  2. 异常信息:${xxx} 字段必填
  3. 标准化信息输出,都可以使用占位符替换

由于Mybatis采用是${}方式替换,那我们先默认使用占位符${}方式,

例: begin-${first}-${second}-${third}-end.
替换后:begin-111-222-333-end.

其中配置属性为

HashMap<String, String> properties = new HashMap<String, String>();
properties.put("first", "111");
properties.put("second", "222");
properties.put("third", "333");

算法过程

  1. 搜索起始占位符${
    在这里插入图片描述
  2. 保存offset与start之间的字符串
    在这里插入图片描述
  3. 搜索结束占位符}

在这里插入图片描述
4. 找出起始占位符与结束占位符之间的字符串expression,并通过expression替换对应的占位符

在这里插入图片描述
5. 重新设置offset与start位置,重复步骤一
在这里插入图片描述

占位符基本替换功能 v0.1

占位符替换实现

public class MyGenericTokenParser {

	private final String openToken = "${";
	private final String closeToken = "}";
	private Map<String, String> variables = new HashMap<String, String>();

	public MyGenericTokenParser(Map<String, String> variables) {
		this.variables.putAll(variables);
	}

	public String parse(String text) {
		if (text == null || text.isEmpty()) {
			return "";
		}
		// search open token
		int offset = 0;
		int start = text.indexOf(openToken, offset);
	
		char[] src = text.toCharArray();
		final StringBuilder resultBuilder = new StringBuilder();
		while (start > -1) {
			// found open token. 
			StringBuilder expression = new StringBuilder();
			resultBuilder.append(src, offset, start - offset);
			offset = start + openToken.length();
			
			// let's search close token.
			int end = text.indexOf(closeToken, offset);
			expression.append(src, offset, end - offset);
			resultBuilder.append(variables.get(expression.toString()));
			
			// while
			offset = end + closeToken.length();
			start = text.indexOf(openToken, offset);
		}
		
		if (offset < src.length) {
			resultBuilder.append(src, offset, src.length - offset);
		}
		return resultBuilder.toString();
	}
}

测试类

public class MyGenericTokenParserTest {
	public static void main(String[] args) {
		HashMap<String, String> properties = new HashMap<String, String>();
		properties.put("first", "111");
		properties.put("second", "222");
		properties.put("third", "333");
		MyGenericTokenParser parser = new MyGenericTokenParser(properties);

		System.out.println(parser.parse("begin-${first}-${second}-${third}-end."));
	}
}

测试结果

begin-111-222-333-end.

占位符替换规则自定义功能 v0.2

平时我们使用占位符替换有很多种方式,除了${},常见的还有

  1. 字符串占位符方式,String.format("%s %d", "张三", 40);
  2. MessageFormat格式化方式,MessageFormat.format("{0} {1}", “张三”, 40)

接下来我们就实现动态设置占位符替换规则

占位符替换实现修改,添加接口MyTokenHandler

public class MyGenericTokenParser2 {
	private final MyTokenHandler handler;

	public MyGenericTokenParser2(MyTokenHandler handler) {
		this.handler = handler;
	}

	public String parse(String text) {
		if (text == null || text.isEmpty()) {
			return "";
		}
		
		String openToken = handler.getOpenToken();
		String closeToken = handler.getCloseToken();
		
		// search open token
		int offset = 0;
		int start = text.indexOf(openToken, offset);

		char[] src = text.toCharArray();
		final StringBuilder resultBuilder = new StringBuilder();
		while (start > -1) {
			// found open token.
			StringBuilder expression = new StringBuilder();
			resultBuilder.append(src, offset, start - offset);
			offset = start + openToken.length();

			// let's search close token.
			int end = text.indexOf(closeToken, offset);
			expression.append(src, offset, end - offset);
			resultBuilder.append(handler.handleToken(expression.toString()));

			// while
			offset = end + closeToken.length();
			start = text.indexOf(openToken, offset);
		}

		if (offset < src.length) {
			resultBuilder.append(src, offset, src.length - offset);
		}
		return resultBuilder.toString();
	}
}

MyTokenHandler定义

public interface MyTokenHandler {
	/**
	 * <p>Description: 起始占位符</p>
	 */
	String getOpenToken();

	/**
	 * <p>Description: 结束占位符</p>
	 */
	String getCloseToken();

	/**
	 * <p>Description: 占位符内容替换</p>
	 */
	String handleToken(String content);
}

${}默认占位符实现

public class MyDefaultTokenHandler implements MyTokenHandler {
	private final String openToken;
	private final String closeToken;
	private final Properties variables;

	public MyDefaultTokenHandler(String openToken, String closeToken, Properties variables) {
		this.openToken = openToken;
		this.closeToken = closeToken;
		this.variables = variables;
	}

	@Override
	public String handleToken(String content) {
		String property = variables.getProperty(content);
		if (property == null) {
			return openToken + content + closeToken;
		}
		return property;
	}

	@Override
	public String getOpenToken() {
		return openToken;
	}

	@Override
	public String getCloseToken() {
		return closeToken;
	}
}

测试类MyGenericTokenParserTest2

public class MyGenericTokenParserTest2 {
	public static void main(String[] args) {
		Properties properties = new Properties();
		properties.put("first", "111");
		properties.put("second", "222");
		properties.put("third", "333");
		MyDefaultTokenHandler handler = new MyDefaultTokenHandler("${", "}", properties);
		MyGenericTokenParser2 parser = new MyGenericTokenParser2(handler);
		System.out.println(parser.parse("begin-${first}-${second}-${third}-end."));
	}
}

测试结果

begin-111-222-333-end.

数组风格占位符替换

public class MyStringArrayTokenHandler implements MyTokenHandler {
	private final String openToken;
	private final String closeToken;
	private final String[] variables;

	public MyStringArrayTokenHandler(String openToken, String closeToken, String[] variables) {
		this.openToken = openToken;
		this.closeToken = closeToken;
		this.variables = variables;
	}

	@Override
	public String handleToken(String index) {
		String property = variables[Integer.parseInt(index)];
		if (property == null) {
			return openToken + index + closeToken;
		}
		return property;
	}

	@Override
	public String getOpenToken() {
		return openToken;
	}

	@Override
	public String getCloseToken() {
		return closeToken;
	}
}

测试类MyGenericTokenParserTest3

public class MyGenericTokenParserTest3 {
	public static void main(String[] args) {
		String[] mapping = new String[] {"111", "222", "333"};
		MyStringArrayTokenHandler handler = new MyStringArrayTokenHandler("{", "}", mapping);
		MyGenericTokenParser2 parser = new MyGenericTokenParser2(handler);
		System.out.println(parser.parse("begin-{0}-{1}-{2}-end."));
	}
}

测试结果

begin-111-222-333-end.

其他功能 v0.3

  1. 兼容起始占位符与结束占位符不全的情况
  2. 兼容转义符号

这里不再实现,上述即为Mybatis类org.apache.ibatis.parsing.GenericTokenParser的全部功能,感兴趣可以自行实现,实现后可以满足以下结果

assertEquals("James T Kirk reporting.", parser.parse("${first_name} ${initial} ${last_name} reporting."));
assertEquals("Hello captain James T Kirk", parser.parse("Hello captain ${first_name} ${initial} ${last_name}"));
assertEquals("James T Kirk", parser.parse("${first_name} ${initial} ${last_name}"));
assertEquals("JamesTKirk", parser.parse("${first_name}${initial}${last_name}"));
assertEquals("{}JamesTKirk", parser.parse("{}${first_name}${initial}${last_name}"));
assertEquals("}JamesTKirk", parser.parse("}${first_name}${initial}${last_name}"));

assertEquals("}James{{T}}Kirk", parser.parse("}${first_name}{{${initial}}}${last_name}"));
assertEquals("}James}T{Kirk", parser.parse("}${first_name}}${initial}{${last_name}"));
assertEquals("}James}T{Kirk", parser.parse("}${first_name}}${initial}{${last_name}"));
assertEquals("}James}T{Kirk{{}}", parser.parse("}${first_name}}${initial}{${last_name}{{}}"));
assertEquals("}James}T{Kirk{{}}", parser.parse("}${first_name}}${initial}{${last_name}{{}}${}"));

assertEquals("{$$something}JamesTKirk", parser.parse("{$$something}${first_name}${initial}${last_name}"));
assertEquals("${", parser.parse("${"));
assertEquals("${\\}", parser.parse("${\\}"));
assertEquals("Hiya", parser.parse("${var{with\\}brace}"));
assertEquals("", parser.parse("${}"));
assertEquals("}", parser.parse("}"));
assertEquals("Hello ${ this is a test.", parser.parse("Hello ${ this is a test."));
assertEquals("Hello } this is a test.", parser.parse("Hello } this is a test."));
assertEquals("Hello } ${ this is a test.", parser.parse("Hello } ${ this is a test."));
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值