1. 简介
模板引擎是为了解决用户界面(显示)与业务数据(内容)分离而产生的,它可以生成特定格式的文档,如HTML、XML等。常见的模板引擎有FreeMarker和Thymeleaf等。
FreeMarker通过特定的语法,如${参数},将数据注入模板中,实现动态内容的生成。它适用于生成复杂格式的Excel文件、PDF文档等。
Thymeleaf则提供标准和Spring标准两种方言,可以直接套用模板实现JSTL、OGNL表达式效果。它特别适用于与SpringMVC集成的项目。
JTE(Java Template Engine)是一个轻量级的模板引擎,专为Java应用设计。它采用了独特的DSL(领域特定语言)来简化模板编写过程,使得模板更加简洁易读。JTE支持强大的继承和组合机制,这不仅有助于构建复杂的页面结构,还能有效减少重复代码。此外,JTE还提供了良好的错误报告功能,帮助开发者快速定位和解决问题,提高了开发效率。总之,无论是小型网站还是大型企业级应用,JTE都能提供灵活且高效的解决方案,是值得尝试的一款模板引擎。
JTE性能
按设计,jte 提供了非常快的输出速度。这是一个包含了 jte 的 mbosecke/template-benchmark 的分支版本,在 AMD Ryzen 5950X(单线程)上运行。
图片
mbosecke/template-benchmark github地址如下:https://github.com/casid/template-benchmark/
高并发
这是与上面相同的基准测试,但线程数被设置为@Threads(16),以充分利用所有核心。jte几乎没有序列化瓶颈,并且在具有多个CPU核心的服务器上能够非常高效地并发运行:
图片
2. 实战案例
2.1 快速入门
定义数据模型
public class Page {
private String title ;
private String description ;
// getters, setters
}
定义jte模板
@import com.pack.jte.test.Page
@param Page page
<html>
<head>
<meta charset="UTF-8">
@if(page.getDescription() != null)
<meta name="description" content="${page.getDescription()}">
@endif
<title>${page.getTitle()}</title>
</head>
<body>
<h1>${page.getTitle()}</h1>
<p>欢迎使用JTE模板引擎</p>
</body>
</html>
- @import 直接转换为 Java 或 Kotlin 的导入语句,在这种情况下,使得 com.pack.jte.test.Page 被模板识别。
- @param Page page 是需要传递给此模板的参数。
- @if / @endif 构成了一个条件块。括号内的内容 (page.getDescription() != null) 是标准的 Java 代码。
- ${} 将内容写入底层模板输出,类似于其他多种模板引擎中的用法。
渲染模板
CodeResolver codeResolver = new DirectoryCodeResolver(Path.of("target/classes/templates/jte")) ;
TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html) ;
StringOutput so = new StringOutput();
templateEngine.render("test.jte", new Page("xxxooo", "这里是馍馍社交"), so) ;
System.err.println(so.toString()) ;
控制台输出结果
图片
上面代码中的TemplateEngine实现有很多,你可根据不同的场景选择合适的输出模板。
2.2 模板语法
数据显示
要在模板中显示数据,请用 ${} 将其包裹起来:
@import my.Model
@param Model model
Hello ${model.name}!
而对应的模型Model则如下:
package my ;
public class Model {
public String name = "Pack" ;
}
上述模板的输出将是 Hello Pack!
${} 可用来输出以下类型:
- String
- Enum
- boolean,byte,short,int,long,float,double,char
- 任意实现了gg.jte.Content的类
注意:出于安全考虑,不会自动进行 .toString() 转换。不支持的类型会产生编译错误。
if语句块
你可以使用关键字 @if、@elseif、@else 和 @endif 来构造 if 语句。这些关键字可直接转换为 Java 对应的关键字:
@if(model.entries.isEmpty())
I have no entries!
@elseif(model.entries.size() == 1)
I have one entry!
@else
I have ${model.entries.size()} entries!
@endif
自 Java 14+ 起,还可以对 instanceof 使用模式匹配:
@if (model instanceof SubModel subModel)
${subModel.getSpecial()}
@endif
就像写Java代码一样。
循环
除了 if 语句,jte 还提供了 @for 和 @endfor 关键字,用于循环遍历可迭代数据。同样,@for 可以直接转换为 Java 或 Kotlin 的对应关键字:
@for(Entry entry : model.entries)
<li>${entry.title}</li>
@endfor
@for(var entry : model.entries)
<li>${entry.title}</li>
@endfor
@for(int i = 0; i < 10; ++i)
<li>i is ${i}</li>
@endfor
循环时,你可以使用 gg.jte.support.ForSupport 类获取有关循环的信息,例如你是在循环的第一次迭代还是最后一次迭代。
@import gg.jte.support.ForSupport
@for(var entryLoop : ForSupport.of(model.entries))
<tr class="${(entryLoop.getIndex() + 1) % 2 == 0 ? "even" : "odd"}">
${entryLoop.get()}
</tr>
@endfor
自 jte 3.0 起,可以在 @endfor 之前使用 @else。如果循环中没有遍历任何元素,@else 内容就会渲染。这对显示空列表状态非常有用,无需额外的 @if。例如
@for(var item : datas)
<tr>
<td>${item.getName()}</td>
<td>${item.getQuantity()}</td>
</tr>
@else
<tr>
<td colspan="2">本月销售总数</td>
</tr>
@endfor
这功能不错误。
注释
jte 允许你在模板中定义注释。
<%-- 这里是注释信息 --%>
注意:模板输出中不包含 jte 注释。
模板调用
要在模板之间共享共同功能,可以调用其他模板。所有模板都必须位于 jte 根目录下。
通用模板定义templates/jte/common.jte
@import com.pack.jte.test.Entry
@param Entry entry
@param boolean verbose
<h2>${entry.getTitle()}</h2>
@if(verbose)
<h3>xxxooo</h3>
@endif
在其它模板中调用该模板。
@template.common(page.getEntry(), false)
这里@template后面是你要调用模板的完整路径。类似方法调用,该模板中需要什么数据,直接在这里传入。
模板渲染示例:
CodeResolver codeResolver = new DirectoryCodeResolver(Path.of("target/classes/templates/jte")) ;
TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html) ;
TemplateOutput so = new StringOutput();
Page page = new Page("xxxooo", "这里是馍馍社交");
page.setEntry(new Entry("你好中国")) ;
以上我们介绍了常用的一些语法,还有其它如需要了解可以查看官网。
2.3 在Spring Boot中的应用
我们只需要引入以下的依赖即可,根据你使用Spring Boot的版本,支持2.x、3.x
<dependency>
<groupId>gg.jte</groupId>
<artifactId>jte</artifactId>
<version>3.1.12</version>
</dependency>
<dependency>
<groupId>gg.jte</groupId>
<artifactId>jte-spring-boot-starter-3</artifactId>
<version>3.1.12</version>
</dependency>
配置文件如下配置
gg:
jte:
developmentMode: true
# 生产环境设置,与上面的不能同时设置
usePrecompiledTemplates: false
# 注意这里的路径,如果你打成的jar运行,则你应该在你当前jar所在目录同级建立对应的目录
templateLocation: target/classes/templates/jte
templateSuffix: .jte
设置了模板文件的路径及文件的后缀。同时设置了当前为开发模式。
我们这里以上面的test.jte为例演示
@Controller
@RequestMapping("/jte")
public class TestController {
@GetMapping("")
public String view(Model model, HttpServletResponse response) {
Page page = new Page("xxxooo", "这里是馍馍社交");
page.setEntry(new Entry("你好中国")) ;
model.addAttribute("page", page);
return "test";
}
}
页面展示:
图片
除了要注意模板路径问题外,其它就像以前一样该怎么写就怎么写,模板数据模型的使用方式都一样的。