一个类型安全的 Java Mustache 模板引擎。
模板被编译成可读的 Java 源代码,值绑定在编译时静态检查。
该文档也位于 javadoc.io,但不像上面那样聚合。聚合的 javadoc 是首选的文档形式,本 README 的其余部分主要用于 宣传 营销目的。
对于之前的版本:
https://jstach.io/doc/jstachio/VERSION/apidocs
其中 VERSION 是你想要的版本号。
无逻辑 Mustache (v 1.3) 语法。
今天就能获得类似 JEP 430 的支持,而且更加强大。
模板编译成 Java 代码
值绑定在编译时静态检查。
方法、字段和 getter 方法可以在模板中引用。
带有上下文信息的友好错误消息。
零配置。无需插件或调整。一切都在标准 javac 下完成,兼容任何 IDE 和/或构建系统。
支持非 HTML 模板。支持的转义内容类型集是可扩展的。
通过 Mustache 继承规范支持布局。
通过 ServiceLoader 提供回退渲染服务扩展点
自定义允许输出的类型,否则会产生编译错误(避免在不友好 toString 的类上调用 toString)。
用于变量运行时自定义 toString 的格式化器
向生成的代码添加额外的 implements 接口,以实现类似 trait 的附加功能(@JStacheInterfaces)
强大的 Lambda 支持
支持 Map<String, ?>
支持 Optional<?>
与 JMustache 和 Handlebars 列表索引扩展兼容(如 -first、、)
本项目不以成为最快的 Java 模板引擎为目标!
(不过它目前是我所知道的、在本 README 上次更新时最快的)
虽然模板语言中性能并不是那么重要(它很少成为瓶颈),但 JStachio 非常快:
https://github.com/agentgt/template-benchmark


@JStache(template = """
{{#people}}
{{message}} {{name}}! You are {{#ageInfo}}{{age}}{{/ageInfo}} years old!
{{#-last}}
That is all for now!
{{/-last}}
{{/people}}
""")
public record HelloWorld(String message, List<Person> people) implements AgeLambdaSupport {
}
public record Person(String name, LocalDate birthday) {
}
public record AgeInfo(long age, String date) {
}
public interface AgeLambdaSupport {
@JStacheLambda
default AgeInfo ageInfo(Person person) {
long age = ChronoUnit.YEARS.between(person.birthday(), LocalDate.now());
String date = person.birthday().format(DateTimeFormatter.ISO_DATE);
return new AgeInfo(age, date);
}
}
@Test
public void testPerson() throws Exception {
Person rick = new Person("Rick", LocalDate.now().minusYears(70));
Person morty = new Person("Morty", LocalDate.now().minusYears(14));
Person beth = new Person("Beth", LocalDate.now().minusYears(35));
Person jerry = new Person("Jerry", LocalDate.now().minusYears(35));
String actual = JStachio.render(new HelloWorld("Hello alien", List.of(rick, morty, beth, jerry)));
String expected = """
Hello alien Rick! You are 70 years old!
Hello alien Morty! You are 14 years old!
Hello alien Beth! You are 35 years old!
Hello alien Jerry! You are 35 years old!
That is all for now!
""";
assertEquals(expected, actual);
}
<properties>
<io.jstach.version>0.6.0-SNAPSHOT</io.jstach.version>
</properties>
...
<dependencies>
<dependency>
<groupId>io.jstach</groupId>
<artifactId>jstachio</artifactId>
<version>${io.jstach.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source> <!-- 17 是最低版本 -->
<target>17</target> <!-- 17 是最低版本 -->
<annotationProcessorPaths>
<path>
<groupId>io.jstach</groupId>
<artifactId>jstachio-apt</artifactId>
<version>${io.jstach.version}</version>
</path>
<!-- 其他注解处理器 -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
注意:注解 jar (jstachio-annotation) 会间接引入
dependencies {
implementation 'io.jstach:jstachio:VERSION'
annotationProcessor 'io.jstach:jstachio-apt:VERSION'
}
{{#name}}
<p>Name: {{.}}, Name Length is {{length}}</p>
{{/name}}
<p>Age: {{ age }}</p>
<p>Achievements:</p>
<ul>
{{#array}}
<li>{{.}}</li>
{{/array}}
</ul>
{{^array}}
<p>No achievements</p>
{{/array}}
<p>Items:</p>
<ol>
{{#list1}}
<li>{{value}}</li>
{{/list1}}
</ol>
以下类可用于提供实际数据来填充上述模板。
@JStache(
// 指向 src/main/resources/user.mustache 文件
path = "user.mustache",
// 或者你也可以内联模板
template = "",
)
public record User(String name, int age, String[] array, List<Item<String>> list) {
public static class Item<T> {
private final T value;
public Item(T value) {
this.value = value;
}
T value() {
return value;
}
}
}
使用上述代码,将自动生成新类 UserRenderer。此类可用于渲染填充了实际数据的模板。要渲染模板,可以使用以下代码:
class Main {
public static void main(String[] args) throws IOException {
User user = new User("John Doe", 21, new String[] {"Knowns nothing"}, list);
StringBuilder appendable = new StringBuilder();
JStachio.render(user, appendable);
}
}
运行此代码的结果将是
<p>Name: John Doe, Name Length is 8</p>
<p>Age: 21</p>
<p>Achievements:</p>
<ul>
<li>Knowns nothing</li>
</ul>
<p>Items:</p>
<ol>
<li>helmet</li>
<li>shower</li>
</ol>
引用不存在的字段或具有不可渲染类型的字段,都会导致编译时错误。这些错误会在项目的编译时报告,同时也会报告 Java 源代码中的其他可能错误。
target/classes/user.mustache:5: error: Field not found in current context: 'age1'
<p>Age: {{ age1 }} ({{birthdate}}) </p>
^
symbol: mustache directive
location: mustache template
target/classes/user.mustache:5: error: Unable to render field: type error: Can't render data.birthdate expression of java.util.Date type
<p>Age: {{ age }} ({{birthdate}}) </p>
^
symbol: mustache directive
location: mustache template
更多示例请参见 test/examples 项目。
枚举本质上具有布尔键,即枚举的名称(Enum.name()),可用作条件节。
假设 light 是一个枚举,例如:
public enum Light {
RED,
GREEN,
YELLOW
}
你可以像模式匹配一样根据枚举进行条件选择:
{{#light.RED}}
STOP
{{/light.RED}}
{{#light.GREEN}}
GO
{{/light.GREEN}}
{{#light.YELLOW}}
Proceeed with caution
{{/light.YELLOW}}
JStachio 与 handlebars 和 JMustache 的迭代节索引键兼容。
-first 是一个布尔值,当你在第一个项目时为真-last 是一个布尔值,当你在可迭代对象的最后一个项目时为真-index 是一个从 1 开始的索引。第一个项目将是 1 而不是 0JStachio 以类似于 JMustache 的方式支持 Lambda 节调用。只需用 @JStacheLambda 标记你的方法,返回的模型将用于渲染 Lambda 节的内容。上下文栈的顶部可以传递给 Lambda。
与规范不同,JStachio 不支持返回动态模板然后针对上下文栈进行渲染。但是,调用方可以通过更改 Lambda 节的内容来实现动态输出,因为该节的内容充当内联模板。
这个想法是创建一个模板引擎,结合 mustache 无逻辑哲学与 Java 的单一职责和静态类型。完全的编译时语法和数据绑定检查是主要要求。
目前,模板会生成 Java 代码。生成的 Java 代码应始终能编译通过。如果无法从某个模板生成有效的 Java 代码,则应生成友好的编译时错误,指向模板文件。用户不应看到生成的 Java 代码。
原始 mustache 使用 Javascript 对象来定义渲染上下文。所选 Javascript 对象的字段与模板字段绑定。
静态 mustache 使用 Java 对象来定义渲染上下文。模板字段的绑定在编译时定义并检查。缺失字段为编译时错误。
JStachio 采用 BSD 3-Clause 许可证。
-last-index除 JStachio 自身外零依赖
还有一个绝对零运行时依赖的选项(即生成所有所需代码,甚至运行时也不需要 jstachio)。无需使用 Maven shade 来处理注解处理器和其他零依赖项目。对于 Graal VM 原生项目也很有用,以尽可能减小体积。
对 Spring 框架的一流支持(即项目本身会提供插件,而不是依赖辅助项目)