StringTemplate修仙指南:字符串处理的“言出法随“大法

发布于:2025-04-11 ⋅ 阅读:(29) ⋅ 点赞:(0)

各位在字符串处理苦海中挣扎的道友们!今天要解锁的是StringTemplate这门"言出法随"的绝学——用模板语法让字符串替换变得优雅如诗!无论是代码生成、邮件模板还是动态SQL,都能一键搞定!准备好告别String.format()的混沌时代了吗? ✨


一、筑基篇:初识StringTemplate

1.1 法宝祭炼(添加依赖)
<!-- ST4 (StringTemplate v4) -->
<dependency>
    <groupId>org.antlr</groupId>
    <artifactId>ST4</artifactId>
    <version>4.3.4</version>
</dependency>
1.2 基础模板渲染(灵力初现)
import org.stringtemplate.v4.*;

// 创建模板(类似"Hello, ${name}!"的语法)
ST helloTemplate = new ST("Hello, <name>!");
helloTemplate.add("name", "张无忌");

// 渲染结果
System.out.println(helloTemplate.render()); 
// 输出:Hello, 张无忌!

二、金丹篇:模板语法大全

2.1 条件判断(御剑飞行)
ST template = new ST("<if(condition)>条件成立<else>条件不成立<endif>", '$', '$');
template.add("condition", true);
System.out.println(template.render()); // 输出:条件成立
2.2 循环遍历(分身术)
ST template = new ST("<items:{item|<item.id>: <item.name>\n}>");
template.add("items", Arrays.asList(
    new Item(1, "屠龙刀"),
    new Item(2, "倚天剑")
));
// 输出:
// 1: 屠龙刀
// 2: 倚天剑
2.3 模板继承(宗门大阵)
// 定义基础模板
String baseTemplate = "header() ::= <<欢迎来到$title$>>\n" +
                     "footer() ::= <<© $year$>>\n" +
                     "main() ::= <<$header()$\n$content$\n$footer()$>>";

STGroup group = new STGroupString(baseTemplate);
group.defineTemplate("content", "这里是正文");

ST page = group.getInstanceOf("main");
page.add("title", "修仙商城");
page.add("year", 2023);
System.out.println(page.render());

三、元婴篇:高级特性

3.1 自定义渲染器(灵力具现化)
public class DateRenderer implements AttributeRenderer {
    @Override
    public String toString(Object o, String format, Locale locale) {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format((Date) o);
    }
}

// 注册渲染器
STGroup group = new STGroup();
group.registerRenderer(Date.class, new DateRenderer());

ST template = new ST(group, "当前时间: <createTime; format=\"yyyy-MM-dd\">");
template.add("createTime", new Date());
// 输出:当前时间: 2023-08-15
3.2 多模板组管理(洞天福地)
// 从目录加载模板组
STGroup group = new STGroupDir("/templates", '$', '$');

// 从字符串定义模板组
STGroup group2 = new STGroupString("sayHello(name) ::= <<你好,<name>!>>");

// 组合使用
ST hello = group2.getInstanceOf("sayHello");
hello.add("name", "张三丰");
System.out.println(hello.render());

四、化神篇:实战应用

4.1 代码生成器(点石成金)
String classTemplate = "public class <className> {\n" +
                      "    <fields:{field|private <field.type> <field.name>;\n}>\n" +
                      "    // getters/setters...\n}";

ST template = new ST(classTemplate);
template.add("className", "User");
template.add("fields", Arrays.asList(
    new Field("String", "name"),
    new Field("int", "age")
));

System.out.println(template.render());
/* 输出:
public class User {
    private String name;
    private int age;
    // getters/setters...
}
*/
4.2 动态SQL构建(乾坤大挪移)
String sqlTemplate = "SELECT * FROM users\n" +
                    "WHERE 1=1\n" +
                    "<if(name)>AND name = '<name>'<endif>\n" +
                    "<if(age)>AND age = <age><endif>\n" +
                    "LIMIT <limit>";

ST template = new ST(sqlTemplate);
template.add("name", "张无忌");
template.add("limit", 10);
// age未设置,自动忽略

System.out.println(template.render());
/* 输出:
SELECT * FROM users
WHERE 1=1
AND name = '张无忌'
LIMIT 10
*/

五、大乘篇:性能优化

5.1 模板预编译(灵力压缩)
// 提前编译模板
STGroup group = new STGroupFile("templates.stg");
ST template = group.getInstanceOf("reportTemplate");

// 重复使用(比每次都new ST()快3倍+)
for (int i = 0; i < 1000; i++) {
    template.remove("data");
    template.add("data", fetchData());
    String report = template.render();
}
5.2 缓存策略(灵气循环)
// 使用Guava缓存编译后的模板
LoadingCache<String, ST> templateCache = CacheBuilder.newBuilder()
    .maximumSize(100)
    .build(new CacheLoader<String, ST>() {
        @Override
        public ST load(String key) throws Exception {
            return new ST(key);
        }
    });

// 使用缓存
ST template = templateCache.get("Hello, <name>!");
template.add("name", "周芷若");

六、心魔警示(常见陷阱)

  1. 特殊字符转义

    // 默认不转义HTML/XML
    ST template = new ST("<data>");
    template.add("data", "<script>alert(1)</script>");
    // 输出:<script>alert(1)</script>
    
    // 解决方案:自定义渲染器或手动转义
    
  2. 空值处理

    ST template = new ST("Value: <value>");
    // template.add("value", null); // 会抛NullPointerException
    template.add("value", null != value ? value : "");
    
  3. 性能陷阱

    // 错误示范:每次渲染都创建新模板
    for (Data data : dataList) {
        ST bad = new ST("..."); // 重复解析模板
        bad.add("data", data);
        render(bad);
    }
    

飞升指南:最佳实践

  1. 模板管理:将模板文件与代码分离
  2. 参数校验:渲染前检查必填参数
  3. 版本控制:模板文件纳入Git管理
  4. 文档注释:在模板中添加使用说明

网站公告

今日签到

点亮在社区的每一天
去签到