在开发的过程中,可能遇到替换字符串的占位符的场景,如:消息系统中,消息模板的配置;日志系统中日志打印等场景中都会使用到占位符替换。在一些已经成熟的框架中也经常见到占位符替换的例子,如:log4j中的日志打印,Mybatis的sql预编译参数设置等。
那么我们在自研系统的时候,遇到以上提到的场景时,我们该如何去开发呢,接下来我将用两个示例介绍一下。
消息模板
在消息相关的系统开发过程中,我们通常会对用户提供消息模板的配置,如短信模板,通讯软件消息模板,邮件消息模板,站内信消息模板等,用户配置好模板后,可调用消息系统的api,传递消息参数,消息系统根据模板内容对占位符进行替换,发送消息。
- 模板样例
尊敬的:${userName} 您好,您有${integralCount}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!
freemarker
maven
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
代码
@Test
public void test() {
try {
String templateName ="ActivityMessage";
Template template = newTemplate(templateName, getTemplate(), new Configuration(newVersion("2.3.23")));
StringWriter result = newStringWriter();
template.process(getParams(),result);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
public Map<String, Object> getParams() {
Map<String, Object> params =new HashMap<>(8);
params.put("userName","fly");
params.put("integralCount",100.3456);
params.put("activityName","新用户活动");
params.put("activityUrl","jd.com");
return params;
}
public String getTemplate() {
return "尊敬的:${userName} 您好,您有${integralCount?string(\"#.##\")}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!";
}
commons-text
@Test
public void void() {
StringSubstitutor strSubstitutor =new StringSubstitutor(getParams());
String messageContent =strSubstitutor.replace(getTemplate());
System.out.println(messageContent);
}
public Map<String, Object> getParams() {
Map<String, Object> params =new HashMap<>(8);
params.put("userName","fly");
params.put("integralCount",100.3456);
params.put("activityName","新用户活动");
params.put("activityUrl","jd.com");
return params;
}
public String getTemplate() {
return "尊敬的:${userName} 您好,您有${integralCount}积分未使用,现有${activityName}活动,大礼包为您准备好了!戳${activityUrl}参加吧!";
}
日志打印模板
在日志系统的开发中,我们可能有类似于log4j的日志打印的开发需求(log.info("活动基本信息 → {}", JSON.toString(activityInfo))),使用"{}",作为占位符,将参数拼接打印在顺序的位置上,我们可以模仿mybatis的占位,代码如下。
public class LoggerParser {
private LoggerParser() {
}
/**
* 将字符串text中由openToken和closeToken组成的占位符依次替换为args数组中的值
*
* @param openToken left
* @param closeToken right
* @param text text
* @param args args
* @return message
*/
private static String parse(StringopenToken, String closeToken, String text, Object... args) {
if (args == null || args.length<= 0) {
return text;
}
int argsIndex = 0;
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 = newStringBuilder();
StringBuilder expression = null;
while (start > -1) {
if (start > 0 &&src[start - 1] == '\\') {
// this open token isescaped. 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 = newStringBuilder();
} 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 closetoken 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);
break;
}
}
if (end == -1) {
// close token wasnot found.
builder.append(src,start, src.length - start);
offset = src.length;
} else {
///仅仅修改了该else分支下的个别行代码
String value =(argsIndex <= args.length - 1) ?
(args[argsIndex] == null ? "" : args[argsIndex].toString()) :expression.toString();
builder.append(value);
offset = end +closeToken.length();
argsIndex++;
}
}
start =text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset,src.length - offset);
}
return builder.toString();
}
public static String parser(Stringformat, Object... args) {
return parse("{","}", format, args);
}
}
测试
@Test
public void test() {
System.out.println(LoggerParser.parser("您好 -> {}, 欢迎参加 -> {} 活动", "fly", "618大促"));
}
总结
在模板类的展位符的场景下,通常使用相同名称参数替换的方式,我们可以采用freemarker或者commons-text,相对来讲commons-text使用起来代码更加简洁。
在一些类似日志打印场景下,通常使用参数按顺序进行替换,可以使用LoggerParser这种方式,进行格式化参数。