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
由于占位符替换在实现工作中使用频率较高,如
- 短信模块:
${NAME}在${TIME},存钱${MONEY}。【中国人民银行】
- 异常信息:
${xxx} 字段必填
- 等
标准化信息输出
,都可以使用占位符替换
由于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");
算法过程
- 搜索起始占位符
${
- 保存offset与start之间的字符串
- 搜索结束占位符
}
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
平时我们使用占位符替换有很多种方式,除了${}
,常见的还有
- 字符串占位符方式,
String.format("%s %d", "张三", 40);
- 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
- 兼容起始占位符与结束占位符不全的情况
- 兼容转义符号
这里不再实现,上述即为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."));