登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java 22 String Templates 怎么安全拼接文本:模板处理器、转义与输入边界

来源:17golang原创

时间:2026-08-25 18:10:07 388浏览 收藏

Java 22 带来的 String Templates 也就是 JEP 459,核心解决的就是字面文本和变量表达式混写时,把后续处理逻辑统一收口到明确的处理器里,不用开发者分散手动处理拼接逻辑。这个特性在 Java 22 中仍处于第二次预览状态,适合做语言特性实验、理清安全拼接的实现思路,不要看到示例能正常编译,就直接把它当成可以长期依赖的稳定生产 API 使用。

要点速览
  • String Templates 把模板文本、插值表达式和模板处理器组合起来,STR 只是其中一个预定义处理器。
  • 模板插值不会自动把业务输入转化为可信内容,金额、用户名和跳转参数仍然要提前做好类型校验和范围校验。
  • 自定义处理器适合集中实现转义、格式化或者拦截拒绝策略,但抛出的异常必须保留足够上下文,方便快速定位是哪一段输入出了问题。
  • Java 22 运行相关代码必须开启预览开关;正式引入生产项目之前,要先确认目标 JDK 版本、编译参数配置和这个特性后续的官方状态。

Java 22 的变化:拼接不再只有字符串相加

假设服务要生成一条账单通知,旧写法通常是多个 + 连接,或者先把变量塞进 Map 再交给模板引擎。String Templates 的核心思路是把“模板中的文字”和“表达式产生的值”交给处理器,由处理器决定最后生成什么结果。

String customer = "林晓";
int cents = 12800;

String message = STR."客户 \{customer},本期应付 \{cents / 100.0} 元";
System.out.println(message);
// 客户 林晓,本期应付 128.0 元

这段写法必须在开启了预览特性支持的 Java 22 编译环境下才能正常运行。它的价值根本不是少写几个字符串拼接的加号,而是把所有插值点的处理逻辑全部交给了一个可以统一替换的处理器来管控。

Java 22 String Templates 将文字与表达式交给模板处理器再生成通知文本的处理链路

先看清三部分:文字、表达式和处理器

一个模板表达式可以拆成三块:模板文字是固定部分,嵌入表达式负责计算值,处理器负责把两者组合起来。预定义的 STR 返回字符串;FMT 则可以结合格式说明控制输出样式。

String name = "林晓";
double amount = 128.0;

String plain = STR."客户 \{name},金额 \{amount} 元";
String formatted = FMT."客户 \{name},金额 \{amount, number, %.2f} 元";

这里不要把模板处理器简单理解成更智能的字符串拼接工具。如果插值内容是来自请求参数的邮箱、备注或 URL,它们本身仍然是不可信数据;处理器只会按照预设规则处理内容,不可能替业务代码完成权限校验、参数合法性检查和对应上下文的编码操作。

模板能插值,不代表输入可以直接信任

涉及金额的场景最容易暴露输入边界的问题。要是直接把存在浮点数精度问题的金额值插入文本,最后生成的结果很可能出现不符合账单规则的异常小数位;要是把用户备注直接嵌入 HTML 内容,又会把普通文本上下文和 HTML 标签上下文混淆,直接引发安全风险。先完成所有业务层面的领域校验,再交给模板处理器处理会稳妥很多。

static String invoiceLine(String customer, long cents) {
    if (customer == null || customer.isBlank()) {
        throw new IllegalArgumentException("customer is blank");
    }
    if (cents  10_000_000) {
        throw new IllegalArgumentException("cents out of range: " + cents);
    }
    return STR."客户 \{customer},本期应付 \{cents / 100} 元";
}

这段校验逻辑只保证输入符合账单领域的基本范围,并不具备处理 HTML、SQL 或者命令行场景安全风险的能力。最终输出内容会进入什么使用上下文,才决定了后续还需要搭配哪一种编码规则或者对应场景的专用处理器。

用自定义处理器把转义和拒绝策略集中起来

String Templates 的设计允许处理器读取模板片段和所有插值值,再返回自定义类型的结果。生成 HTML 文本时,可以让处理器统一对每一个插值做 HTML 转义;处理 SQL 场景时,处理器应该直接返回参数化查询对象,而不是把值简单转义之后拼接到 SQL 字符串里。

record SafeText(String value) {}

static String htmlText(String value) {
    return value.replace("&", "&")
            .replace("", ">")
            .replace("\"", """)
            .replace("'", "'");
}

// 真实项目中应把转义放进专用模板处理器,而不是每个调用点手写。
String note = htmlText("备注:");

示例里的手写字方法只是为了说明不同模块的责任边界。实际接入 Web 项目时,直接用框架提供的成熟上下文编码能力即可,还要覆盖双引号、单引号、尖括号、换行符和特殊 Unicode 字符的测试场景。

Java String Templates 对可信金额与未校验备注分别处理并在输出前拒绝越界输入的边界对照

预览开关和版本状态不能漏验

Java 22 编译和运行预览特性代码时,需要显式开启预览特性的支持开关:

javac --enable-preview --release 22 InvoiceDemo.java
java --enable-preview InvoiceDemo

命令执行通过只能说明当前 JDK 接受这段实验代码,不等于项目拿到了长期有效的稳定语言契约。JEP 459 是 Java 22 的第二次预览特性,后续 JEP 465 曾计划推进第三次预览,之后 String Templates 整个特性被官方撤回。所以升级或迁移项目时,要把相关代码当成实验性依赖,记录好对应的 JDK 构建版本,提前准备好普通拼接或者成熟模板引擎的回退方案。

把验收样例固定下来

做测试覆盖时,至少要覆盖这些输入场景:正常姓名、空姓名、负金额、超出业务上限的超大金额、包含尖括号的备注、包含换行的备注,以及模板处理器抛出异常的异常流场景。测试重点不是验证某个 API 能不能调用成功,而是要确认输出格式和拒绝策略不会在后续迭代中被悄悄修改。

assertEquals("客户 林晓,本期应付 128 元", invoiceLine("林晓", 12800));
assertThrows(IllegalArgumentException.class, () -> invoiceLine("", 12800));
assertThrows(IllegalArgumentException.class, () -> invoiceLine("林晓", -1));

常见问题

String Templates 是 Java 22 的稳定特性吗?

并不是。Java 22 中它仍属于预览特性,项目必须显式开启预览支持才能使用,后续高版本 JDK 的特性状态还发生过调整,完全不能按稳定 API 做长期生产承诺。

STR 会自动防止注入吗?

不会自动处理。它只负责按照预设逻辑处理模板片段和插入的表达式值,不会替你判断当前内容要放到 HTML、SQL、日志还是 URL 上下文里,自动匹配对应的安全规则。

什么时候不该使用 String Templates?

目标运行环境没有对应版本的 JDK、项目规范禁止使用预览特性,或者团队没有精力维护兼容回退路径时,普通拼接、参数化 API 或者经过大量生产验证的成熟模板引擎是更合适的选择。

自定义模板处理器应该先做什么?

上手尝试自定义处理器的时候,先明确要返回的类型、异常处理策略和输入使用上下文,再用边界用例验证转义逻辑、空值处理、换行兼容和超长输入的表现,不要一上来就写非常复杂的嵌套语法。

String Templates 最值得借鉴的是「把拼接处理策略显式交给专用处理器收口」这个设计思路,而不是某一行特殊的插值语法。用 Java 22 做特性实验时,把预览开关配置、业务领域校验、上下文编码和回退兼容方案一起纳入验收清单,才不至于把一次新语法的尝鲜,误当成覆盖全场景的生产安全保障。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>