Beetl

Beetl

Beetl是Bee Template Language的縮寫,它絕不是簡單的另外一種模板引擎,而是新一代的模板引擎,它功能強大,性能良好,秒殺當前流行的模板引擎。而且還易學易用。

基本介紹

Beetl,是Bee Template Language的縮寫,它絕不是簡單的另外一種模板引擎,而是新一代的模板引擎,它功能強大,性能良好,秒殺當前流行的模板引擎。而且還易學易用。

概述

1 JS類似語法和使用習慣,30分鐘學會。

2 功能齊全,與Freemarker不相上下。還有更多的獨特功能,包括Freemarker,Velocity等成10+年以上的成熟模板引擎都尚未具備。

3 高性能,超過Freemarker2-4倍。主要通過位元組輸出,強類型編譯來實現。

4 可靠性,套用於電商,企業系統,入口網站,經過一年多的使用,已經非常穩定。

其他特點

1 整個大小410K,其他模板語言有的功能,Beetl基本上都包含。

2 雙引擎,包含了解釋執行引擎,也包含了運行時編譯引擎.即適合代碼生成,也適合高並發,大訪問量的電商網站.編譯引擎採用了類型推測,不需要用戶在模板中申明變數的類型,能在運行時刻獲知並編譯,模板引擎技術先進。

3 詳細的錯誤提示,包括行數,錯誤符號,以及上下相關行,以及錯誤原因,錯誤檔案名稱。

4 beetl提供安全輸出,在意外情況下,頁面仍然能得到渲染 。

5 自定義函式,格式化函式,標籤庫。允許自定義虛擬屬性,無需在java代碼中為Pojo添加額外屬性專門用於顯示 。

6 自定義占位符號,控制語句符號,將對模板的侵入性降到最低。

7 輸出不會產生多餘的空格和換行。其他模板語言或者JSP將不可避免的產生此問題。

8支持單獨測試,在MVC體系中,缺少C和M,也能單獨測試V,即beetl模板。

9 與Servlet,Spring MVC,Struts2,JFinal等框架整合。

實現特徵

現代模版引擎新方向和Beetl實現

Java模板引擎已經發展了10餘年,使用模板引擎者和模板引擎開發者都不思進取,得過且過,變化一直不大。畢竟,有技術含量值得屌絲追求的還是在後台。譬如早期的EJB,後來的WebService,SSH,還有雲計算,NOSQL等。但是,模板引擎沒有變化並不代表已經非常成熟,它跟其他技術一樣,也需要更新換代。程式設計師越來越認識到,模板開發在工作中占了較大工時,模板在渲染時占了CPU較大份額。國內外,已經有聰明的程式設計師意識到現在流行的模板引擎有些陳舊,不能滿足更好的模板套用,紛紛提出新的模板引擎需求,甚至有些有才智的程式設計師已經開發出更好的模板引擎供大家使用。

Beetl開發者 總結了一執行緒序員的建議,列出了現代模板引擎應該具有的7大特徵,並使用新一代Beetl模板語言作為說明。

特徵一,模板語言應該有較少的侵入性

由於模板語言總是“嵌入”到原來的檔案中,減少對原有檔案的侵入性對模板的維護是非常有利的。有的模板引擎的占位符號是“$變數名”,這就是一個典型侵入性過強的例子。因為對於html檔案來說,一些js變數是以$開頭(如Jquery),因此這樣的模板即不好維護,而且模板引擎會誤認為某些js變數也是占位符而導致解析混亂。

Js代碼

var userId = $userId;

流行的Freemarker沒有此問題,但更大的問題是它的控制語句定界符是"<#" ">",這樣,進過它更改的HTML模板到處充滿了<#符號,你再無法使用IE瀏覽器,Dreamweaver編輯工具進行編輯了。因為它不在是”HTML“了。
還有些以HTML為主要對象的模板引擎,把控制語句作為DOM一個節點屬性,這樣看似對原有檔案侵入性低,使用IE或者Dreamweaver仍然能打開編輯,但仍然混淆了模板語言和HTML檔案。我以為仍然不利於模板的維護。

Html代碼

<tr item="user" foreach="usersList" >

</tr>

為了降低模板語言對原有的檔案侵入性,Beetl是如何做的呢?

Beetl允許自定義占位符號和控制語句,譬如在HTML中,可以設定控制語句定界符是"<!--#" "-->",這在HTML中看似一個注釋片段,因此侵入性就非常低了

Html代碼

<!--# for(user in usersList){ -->

<tr>

<td>${user. name}</td>

</tr>

<!--# } -->

你也可以設定自己喜歡的占位符,如最通用的"${" "}",或者我曾經喜愛的"~" "~"。

關於傾入性,還有個這樣的有趣問題,就是自己開發一個界面生成工具,通常採用模板生成jsp代碼,就是很少有人用”模板生成模板代碼“,這是因為對一些陳舊的模板引擎來說,會產生符號衝突。Beetl中則不會出現此問題,其他模板語會有這個問題。

特徵二, 性能良好才能省錢

模板引擎渲染頁面過程會占相當大部分的CPU,特別是一些後台只有少量計算的web套用,比如以資料庫為中心的Web套用。選用高性能的模板引擎,是相當划算的選擇,特別是對那些大的網際網路公司,動不動就數百,數千台web伺服器。省下一些CPU,就剩下一些錢。而且,高性能的模板引擎會給用戶帶來很好的Web使用體驗。

有才智的程式設計師已經把越來越多的提升模板引擎性能的方法套用於現代模板引擎,性能已經成倍的超過了原有的Freemarker,Velcoity。這些提升性能方法有:

模板引擎將模板檔案編譯成class運行。

模板中的靜態部分採用二進制輸出,不需要CPU運行的時候再轉碼

合併模板中的靜態部分一起輸出,而不是每一行每一行輸出

Beetl是為數不多同時具備上面三個最佳化策略的現代模板引擎.對於第一條編譯成class,beetl比其他現代模板引擎走的更遠一點,它是帶類型的編譯,儘量避免反射調用。而且,beetl不需求在模板中申明模板變數的Java類型,是通過運行時刻推測出來的,編譯引擎非常先進。如下是一個模板

Html代碼

<html>

<body>

${user. name}/${user.role}<br/>

<%if(user.role == "admin"){%>

<table>

<tr>

<th>NO.</th>

<th>Title</th>

<th>Author</th>

<th>Publisher</th>

<th>PublicationDate</th>

<th>Price</th>

<th>DiscountPercent</th>

<th>DiscountPrice</th>

</tr>

<%for(book in books){%>

<%if(book.price > 0){%>

<tr>

<td>${book_index + 1}</td>

<td>${book.title}</td>

<td>${book.author}</td>

<td>${book.publisher}</td>

<td>${book.publication,dateFormat="yyyy-MM-dd HH:mm:ss"}</td>

<td>${book.price}</td>

<td>${book.discount}%</td>

<td>${book.price * book.discount / 100}</td>

</tr>

<%}%>

<%}%>

</table>

編譯後的代碼

Java代碼

final User user;

final ArrayList<Book> books;

try{

user = (User)ctx.getVarWithoutException("user");

books = (ArrayList<Book>)ctx.getVarWithoutException("books");

}catch(ClassCastException ex){

//轉入解釋模式執行

throw new VaribaleCastException(ex);

}

try{

out.write(__V0);

out.write(user.getName());

out.write(__V1);

out.write(user.getRole());

out.write(__V2);

out.write(__VCR);

if(user.getRole().equals("admin")){

out.write(__V3);

int book_index = 0;

int book_size = books.size();

for(Book book : books){

if((new BeeNumber(book.getPrice()).compareTo(new BeeNumber(0))>0)){

out.write(__V6);

out.write(__V7);

out.write((new BeeNumber(book_index).add(new BeeNumber(1))));

out.write(__V8);

out.write(__VCR);

out.write(__V9);

out.write(book.getTitle());

/// 忽略其他代碼

private static final byte[] __V13 = new byte[]{0x20,0x20,0x20,0x20,0x3c,0x74,0x64,0x3e};

private static final byte[] __V14 = new byte[]{0x3c,0x2f,0x74,0x64,0x3e};

private static final byte[] __V1 = new byte[]{0x2f};

private static final byte[] __V15 = new byte[]{0x20,0x20,0x20,0x20,0x3c,0x74,0x64,0x3e};

private static final byte[] __V0 = new byte[]{0x3c,0x68,0x74,0x6d,0x6c,0x3e,0xd,0xa,0x3c,0x62,0x6f,0x64,0x79,0x3e,0xd,0xa};

private static final byte[] __V16 = new byte[]{0x3c,0x2f,0x74,0x64,0x3e};

private static final byte[] __V3 = new byte[]

如代碼4行所示,模板變數user即使User對象,這是模板運行時推測出來,無需再模板中申明

如代碼11行,原有檔案中的<html><body>倆行合併成一行輸出。不像傳統JSP,編譯成class是多行輸出,會影響性能的

代碼32以後是實現將靜態文本轉化為二進制,這樣,IO輸出非常快!

Beetl通過採用上面三個策略,性能是以傳統模板引擎的2-4倍。一些現代模板引擎,如HTTL,以及我的好友Green寫的rythm也採用了上面三個最佳化策略,從它們的測試報告中,某些測試性能超過了5倍Freemarker。

特徵三, 良好的錯誤提示

早期在使用JSP過程中,由於JSP運行出錯,但找不到具體出錯原因,這讓我們老一批的開發人員非常痛苦。後來的模板引擎改善了這點,具有良好的錯誤提示,能精確的提示道錯誤的行數,和可能的錯誤原因。新一代模板引擎在這點上做的更好,以Beetl為例,能顯示錯誤的行數,錯誤的符號,錯誤的原因,以及錯誤所在的檔案,和錯誤上下三行。(並且都是可定製的),譬如如下模板有某處錯誤:

Html代碼

<html>

<body>

${user. name}/${user.role}<br/>

<%if(user.role == "admin"{%>

<table>

<tr>

<th>NO.</th>

在編譯運行時,會報如下錯誤

Java代碼

>>語法錯:缺少符號')',4 行 檔案 \beetl\book.txt

1|<html>

2|<body>

3|${ user. name}/${user.role}<br/>

4|<%if(user.role == "admin"{%>

5|<table>

6| <tr>

這樣,開發者就很容易改正問題

JSP之所以連錯誤行數都無法準確報告出來,主要是JSP會編譯成Class而失去了原有行數信息,在現代模板引擎中,此信息不會丟失,譬如作者的好友Green的模板引擎能將行數保存到生成的原始碼里,可以根據錯誤間接查詢出模板錯誤行數,Beetl在這方面走的更棒,能自動在錯誤時列印出錯誤行數。這是因為beetl生成class代碼的時候,將模板中可能出錯的行數與生成的Class行數關聯起來

Java代碼

/* 行映射*/

protected String lineMap = "-69=26-33=4-38=17-65=25-36=16-37=16-78=31-41=19-45=20-49=21-21=0-20=0-53=22-57=23-28=3-61=24-30=3-";

以69=23為例子,表示class的69行出錯,代表了模板中的26行出錯

特徵四, 支持渲染結果的再處理

JSP標籤庫就是一種渲染結果再處理,譬如可以用JSP標籤完成一個Cache標籤。此標籤可以快取標籤體的內容而不需要每次都去渲染。傳統的模板引擎,如Freemarker也有這個功能。但某些速度很快的模板引擎卻不具備此功能,這是很遺憾的。這樣的模板除了作為代碼生成器或者內容生成器外,並不適合作為動態Web輸出。

Beetl 可以通過倆種方式支持渲染結果再處理,一種是類似JSP,Freemarker的標籤庫,以模板中的布局為例,beetl支持layout標籤函式,可以輕易完成簡單的布局功能,如下例子:

Html代碼

這是child頁面

<%layout('/ext/layout_template.html'){%>

hello,我是${name}

<%}%>

如代碼所示,layout標籤將使用/ext/layout_template.html作為布局頁面,將”{“ ”}“的內容渲染後插入到模板頁面

Beetl還有一種更為先進的渲染結果再處理的方式,即獨一無二的模板變數功能,可以將模板渲染的結果作為模板變數保存起來以備用。如在複雜的布局要求中,通常要求模板頁面的js部分插入到布局頁面頭部,動態html部分插入到布局頁面的某處,採用別的模板引擎,這是幾乎不可能實現的,使用Beetl,則很簡單

Html代碼

<%

var jsPart = {

%>

<script>

//這是js部分,將放在布局頁面的頭部

</script>

<%};%>

<%var htmlPart={%>

<div>

這是html部分,將放在布局頁面的底部

</div>

<%};%>

<%

var layoutParas = {"jsPart":jsPart,"htmlPart":htmlPart};

includeFileTemplate('/ext/complex_layout_template.html',layoutParas){}

%>

如上定義了倆個模板變數jsPart,和 htmlPart,渲染結果暫時保存到這倆個變數,然後再模板布局頁面使用這倆個變數

特徵五, 安全輸出

早期的JSP功能,經常因為空指針或者變數不存在而報錯,這是缺少安全輸出功能,Freemarker就做的特別好,可以用!表示如果變數為空或者不存在則輸出!後面的值。Beetl也提供了同樣的功能,如下模板中

Html代碼

<span>${user. wife. name!"單身"}</span>

<span>${[email protected]}</span>

上面例子第一行表示如果user為空,或者user.wife為空,或者user. wife. name為空,這輸出”單身“。

第二行同樣是安全輸出,只是輸出為java class的一個常量(在beetl中,直接調用class方法和屬性是很簡單的,只要加一個@符號即可,Beetl提供安全管理,架構師可以指定那些class能被調用,哪些class是不能調用,如Runtime類就不能被調用)

遺憾的是,現代有些模板引擎還是不支持安全輸出,只能說要么這些模板引擎的開發者還沒有意識到安全輸出的重要性,要么模板引擎還需要更長一段時間的完善

特徵六,可測試的模板

通常程式設計師測試模板是否正確,在MVC架構中,必須同時具備M V C 三個,只有很少模板引擎可以說只需要M 和 V,不需要C,現代模板引擎應該能做的更好,以Beetl為例,它支持僅有V的情況也能測試模板的正確性,這特別適合水平開發,即模板開發者,和後台開發者不是同一人的情況。

Beetl是怎么做到的呢? 作為Beetl的作者,我花了很大精力才完成僅有V的情況下也能測試模板功能。譬如,如下簡單模板

Html代碼

<span>this is template,${user. name},${sessions['userId']}</span>

現在即沒有User對象,也沒有Web容器提供sessions,更沒有控制層往session里設定一個userId屬性,如何測試此模板呢?

在Beetl中,提供SimpleTemplateTestUtil類用來完成模板測試,他提供倆個輸入參數,第一個是模板,第二個是一個字元串,字元串包含了以json格式定義的變數,輸出就是模板是否渲染無誤,以及渲染結果

Java代碼

String input = "this is template,${user. name},${sessions['userId']}";

String json = "var user = {'name':'joel'},sessions={'userId':'12345'};";

Writer w = new StringWriter();

SimpleTemplateTestUtil util = new SimpleTemplateTestUtil(input, json, w);

util.run();

System.out.println(util.isOk());

System.out.println(w);

輸出是

Java代碼

true

this is template,joel,12345

Beetl的參與者之一 ”一顆草“正在做一個beetl模板線上體驗網站,即將完成,有興趣的人可以再網站上線上寫模板,測試模板效果,這正是基於Beetl提供的可測試模板,這是獨一無二的。

特徵七,簡潔的指令,良好的擴展性

現代模板引擎語言,我以為應該以更簡單的語法,豐富的函式調用為主。去掉那些花哨的語言特性為好。以循環為例子,最為簡單的語法莫過於大家都熟悉的形式,如for(xxx in xxxList), 有的現代模板引擎則是for( xxx << xxxList),<<符號在Java中意為這移位操作,這很容易讓人誤解,其實真的無須這樣語法,看似迷人,其實一點不好用。

還有的模板引擎如Freemarker,"?" ,"!" 滿天飛,一個小小的模板,充滿了?! 符號,是非常彆扭的。

Beetl提供的語法非常簡單,類似JS。能很快的上手,我採用Beetl的項目的同事們,基本上能做到不看Guild文檔而能完成大部分模板的開發。Beetl同時又具備當今流行模板的所有功能(甚至在功能上是超過了這些模板),是通過提供了豐富的函式功能來完成的,以將日期格式轉化輸出為例子,Freemarker提供了如下多的選擇

Java代碼

${openingTime?string.short}

${openingTime?string.medium}

${openingTime?string.long}

${openingTime?string.full}

而Beetl僅僅提供一個dateFormat格式化函式

Java代碼

${lastUpdated,dateFormat="yyyy-MM-dd"}

如果你不滿意此日期格式函式,你也可以非常方便自定義格式化日期函式,如下代碼

Java代碼

group.registerFormat("shortDate", new Format(){

@Override

public Object format(Object data, String pattern)

{

Date d = (Date)data;

return new SimpleDateFormat("yyyy-MM-dd").format(d);

}

}

);

你在模板中,可以調用shortDate格式化函式

Java代碼

${lastUpdated,shortDate}

像Freemarker這樣已經很容易學的模板引擎,我在群里,經常能看到很多人問起Freemarker各種使用問題,對於各位使用者來說,初學的時候仍然是花了很長時間,各種使用潛在的問題讓初學者猝不及防。這都是因為Freemarker過多的語法,以及新手對一門新的語法產生的 語法習俗 不適應導致的 ,我強烈介意那些頭一次使用Freemarker的的程式設計師,謹慎考慮使用Freemarker帶來的問題,儘管它是最流行的,但不代表對你的項目來說,是最理想的

相關詞條

相關搜尋

熱門詞條

聯絡我們