性能排名第一的模板引擎 JTE 在 Spring Boot 中的應(yīng)用
1. 簡介
模板引擎是為了解決用戶界面(顯示)與業(yè)務(wù)數(shù)據(jù)(內(nèi)容)分離而產(chǎn)生的,它可以生成特定格式的文檔,如HTML、XML等。常見的模板引擎有FreeMarker和Thymeleaf等。
FreeMarker通過特定的語法,如${參數(shù)},將數(shù)據(jù)注入模板中,實(shí)現(xiàn)動(dòng)態(tài)內(nèi)容的生成。它適用于生成復(fù)雜格式的Excel文件、PDF文檔等。
Thymeleaf則提供標(biāo)準(zhǔn)和Spring標(biāo)準(zhǔn)兩種方言,可以直接套用模板實(shí)現(xiàn)JSTL、OGNL表達(dá)式效果。它特別適用于與SpringMVC集成的項(xiàng)目。
JTE(Java Template Engine)是一個(gè)輕量級(jí)的模板引擎,專為Java應(yīng)用設(shè)計(jì)。它采用了獨(dú)特的DSL(領(lǐng)域特定語言)來簡化模板編寫過程,使得模板更加簡潔易讀。JTE支持強(qiáng)大的繼承和組合機(jī)制,這不僅有助于構(gòu)建復(fù)雜的頁面結(jié)構(gòu),還能有效減少重復(fù)代碼。此外,JTE還提供了良好的錯(cuò)誤報(bào)告功能,幫助開發(fā)者快速定位和解決問題,提高了開發(fā)效率??傊?,無論是小型網(wǎng)站還是大型企業(yè)級(jí)應(yīng)用,JTE都能提供靈活且高效的解決方案,是值得嘗試的一款模板引擎。
JTE性能
按設(shè)計(jì),jte 提供了非??斓妮敵鏊俣取_@是一個(gè)包含了 jte 的 mbosecke/template-benchmark 的分支版本,在 AMD Ryzen 5950X(單線程)上運(yùn)行。
圖片
mbosecke/template-benchmark github地址如下:https://github.com/casid/template-benchmark/
高并發(fā)
這是與上面相同的基準(zhǔn)測試,但線程數(shù)被設(shè)置為@Threads(16),以充分利用所有核心。jte幾乎沒有序列化瓶頸,并且在具有多個(gè)CPU核心的服務(wù)器上能夠非常高效地并發(fā)運(yùn)行:
圖片
2. 實(shí)戰(zhàn)案例
2.1 快速入門
定義數(shù)據(jù)模型
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 直接轉(zhuǎn)換為 Java 或 Kotlin 的導(dǎo)入語句,在這種情況下,使得 com.pack.jte.test.Page 被模板識(shí)別。
- @param Page page 是需要傳遞給此模板的參數(shù)。
- @if / @endif 構(gòu)成了一個(gè)條件塊。括號(hào)內(nèi)的內(nèi)容 (page.getDescription() != null) 是標(biāo)準(zhǔn)的 Java 代碼。
- ${} 將內(nèi)容寫入底層模板輸出,類似于其他多種模板引擎中的用法。
渲染模板
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()) ;
控制臺(tái)輸出結(jié)果
圖片
上面代碼中的TemplateEngine實(shí)現(xiàn)有很多,你可根據(jù)不同的場景選擇合適的輸出模板。
2.2 模板語法
數(shù)據(jù)顯示
要在模板中顯示數(shù)據(jù),請用 ${} 將其包裹起來:
@import my.Model
@param Model model
Hello ${model.name}!
而對(duì)應(yīng)的模型Model則如下:
package my ;
public class Model {
public String name = "Pack" ;
}
上述模板的輸出將是 Hello Pack!
${} 可用來輸出以下類型:
- String
- Enum
- boolean,byte,short,int,long,float,double,char
- 任意實(shí)現(xiàn)了gg.jte.Content的類
注意:出于安全考慮,不會(huì)自動(dòng)進(jìn)行 .toString() 轉(zhuǎn)換。不支持的類型會(huì)產(chǎn)生編譯錯(cuò)誤。
if語句塊
你可以使用關(guān)鍵字 @if、@elseif、@else 和 @endif 來構(gòu)造 if 語句。這些關(guān)鍵字可直接轉(zhuǎn)換為 Java 對(duì)應(yīng)的關(guān)鍵字:
@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+ 起,還可以對(duì) instanceof 使用模式匹配:
@if (model instanceof SubModel subModel)
${subModel.getSpecial()}
@endif
就像寫Java代碼一樣。
循環(huán)
除了 if 語句,jte 還提供了 @for 和 @endfor 關(guān)鍵字,用于循環(huán)遍歷可迭代數(shù)據(jù)。同樣,@for 可以直接轉(zhuǎn)換為 Java 或 Kotlin 的對(duì)應(yīng)關(guān)鍵字:
@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
循環(huán)時(shí),你可以使用 gg.jte.support.ForSupport 類獲取有關(guān)循環(huán)的信息,例如你是在循環(huán)的第一次迭代還是最后一次迭代。
@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。如果循環(huán)中沒有遍歷任何元素,@else 內(nèi)容就會(huì)渲染。這對(duì)顯示空列表狀態(tài)非常有用,無需額外的 @if。例如
@for(var item : datas)
<tr>
<td>${item.getName()}</td>
<td>${item.getQuantity()}</td>
</tr>
@else
<tr>
<td colspan="2">本月銷售總數(shù)</td>
</tr>
@endfor
這功能不錯(cuò)誤。
注釋
jte 允許你在模板中定義注釋。
<%-- 這里是注釋信息 --%>
注意:模板輸出中不包含 jte 注釋。
模板調(diào)用
要在模板之間共享共同功能,可以調(diào)用其他模板。所有模板都必須位于 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
在其它模板中調(diào)用該模板。
@template.common(page.getEntry(), false)
這里@template后面是你要調(diào)用模板的完整路徑。類似方法調(diào)用,該模板中需要什么數(shù)據(jù),直接在這里傳入。
模板渲染示例:
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("你好中國")) ;
以上我們介紹了常用的一些語法,還有其它如需要了解可以查看官網(wǎng)。
2.3 在Spring Boot中的應(yīng)用
我們只需要引入以下的依賴即可,根據(jù)你使用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
# 生產(chǎn)環(huán)境設(shè)置,與上面的不能同時(shí)設(shè)置
usePrecompiledTemplates: false
# 注意這里的路徑,如果你打成的jar運(yùn)行,則你應(yīng)該在你當(dāng)前jar所在目錄同級(jí)建立對(duì)應(yīng)的目錄
templateLocation: target/classes/templates/jte
templateSuffix: .jte
設(shè)置了模板文件的路徑及文件的后綴。同時(shí)設(shè)置了當(dāng)前為開發(fā)模式。
我們這里以上面的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";
}
}
頁面展示:
圖片
除了要注意模板路徑問題外,其它就像以前一樣該怎么寫就怎么寫,模板數(shù)據(jù)模型的使用方式都一樣的。