Spring Boot 2 精髓学习笔记(五)---Beetl 模板引擎(1)

龚招
2023-12-01

本节介绍MVC 中的视图技术之模板引擎Beetl ,用于渲染模板。

一、Beetl模板引擎

Beetl是2010 年开发井维护至今的一个模板引擎,具有如下特点:

  • 功能完备。作为主流模板引擎,适用于各种应用场景,比如对响应速度有很高要求的大型网站,功能繁多的CMS 管理系统,以及代码生成器等。Beetl 本身还具有很多独特的功能来完成模板的编写和维护。
  • 语法和使用习俗简单: 类似JavaScript 语法和习俗, 但又专门为模板渲染定制,也支持HTML 标签, 使得开发CMS 系统变得比较容易。
  • 超高的性能。Beetl远超过主流Java 模板引擎性能, 引擎性能5~6 倍于Freemarker , 2倍于JSP 。
  • 易于整合。Beetl 能很容易地与各种Web 框架整合,如Spring MVC 、ACT ,国内的Nutz 和JFinal ,还有Struts 、Jodd 、Servlet 等。
  • 支持模板单独开发和测试。在MVC 架构中,即使没有M 和C 部分,也能开发和测试模板。
  • 扩展和个性化。Beet! 支持自定义方法、格式化函数、虚拟属性、标签和HTML 标签。同时也支持自定义占位符和控制语句起始符号。

1、安装Beetl

<dependency>
<groupid>com.ibeetl</groupid>
<artifactid>beetl-framework-starter</artifactid>
<version>1.1.15.RELEASE</version>
</dependency>

在Spring Boot 中, beetl-framework-starter 将自动配置以btl 结尾的所有视图,将自动使用Beetl渲染相应的resources/templates 目录下的视图文件。

2、设置定界符号和占位符

Beetl 支持自定义定界符号和占位符号, 默认使用<%%>作为定界符号,使用${}作为占位符号,也可以配置自己喜爱的占位符号, 常用的有:

@,和回车作为定界符号:
<??> , 类似PHP的符号:
<!一一# >,使用HTML 注释符号作为定界符号,加了一个#符号以区别正常的HTML注释。

可以通过配置文件来设置定界符号, 需要在resources 目录下创建一个beetl.properties 文件,

DELIMITER PLACEHOLDER START=${
DELIMITER PLACEHOLDER END=}
DELIMITER STATEMENT START =@
DELIMITER STATEMENT END=

本节后面都将采用“@”和“回车换行”作为定界符号,占位符使用传统的“${”和“}

3、配置Beetl

B eet I 为了提高渲染性能,会在渲染模板后, 缓存模板的语法解析结果, Beetl 每次渲染前都会检测模板文件是否更新, 如果已经更新,则重新解析模板。由于检测模板是否更新会有一次1/0 操作,因此线上系统可以取消检测,需要在appl ication . propert i es 中添加以下配置:

beetl-beetlsql.dev = false

Beetl 默认配置时自动检测模板是否变化,但有的IDE 并不会将resource/templates 目录
下的文件变化同步到Maven 工程的target 目录下,所以即使文件发生变化, Beetl 也检
测不到。如果出现这种情况,一个通用的办法是将resource 目录设定为src 目录,这样
resource 目录下的任何文件改变都会同步到target 目录下.

在Spring B oot 应用中,所有以btl 结尾的模板都会交给Beetl 模板引擎渲染,如果你的模板更喜欢以html 结尾,需要在application.properies 中添加以下配置项:

beetl.suffix = html

二、使用变量

1、全局变量

全局变量即通过Model 或者ModelAndView 传入的变量,可以在模板或者子模板中使用。在Controller 中通过Mo del 、ModelAndView ,或者直接使用request 设置的变量,都可以在模板中使用,比如:

ModelAndView view = new ModelAndView ("/index.btl");
view.addObject ("user", user) ;
return view;

然后可以在Beetl 模板中使用user 变量。${user . name}

对于Spring Boot 应用来说, Beet! 己经提供了以下默认的全局变量:

变量说明
request 中所有的attribute在模板中可以直接通过name 来引用,比如在Controller 层request.setAttribute (” user ”, user),则在模板中可以直接用$ {user.name}。这里也包括Model 或者ModelAndView 中的所有变量,都会成为模板的全局变量。
Session 提供了Session 会话模板通过session [” name”]或者session . name 引用Session中的变量。注意, Session 并非Serviet 中的标准Session 对象。参考Servi et 来获取HTTPSession 。
request标准的HTTPServletRequest ,可以在模板中引用request 属性( getter ),如 $ { request.requestURL}
parameter读取用户提交的参数,如${ parameter.userld} o
ctxPathWeb 应用中的ContextPath 。在Spring Boot 中, 应用默认是“/” 。通过application.properties 的属性server.context-path 可以配置。
servletWebVariable 的实例,包含了HTTPSession 、HTTPServletRequest 、HTTPServletResponse 三个属性,模板中可以通过request 、response 、session 来引用,如$ { servlet.request.requestURL}
所有groupTemplate 的共享变量

2、局部变量

在模板中定义的变量,只能在当前模板中使用,无法在子模板中使用:

@var salary = user.salary*2;
<span> ${user.name} </span>

Beetl 定义变量的方式与JS 类似,比如:

@var a=O , b = ”你好”, d=true , e=34343.343434343434h ,f = e+a ;
@var list=[1,2,3];
@var map={"name":"xiandafu","age":18,"data":list};

变量类型同JavaScript 类型一样,上面的变量e 是一个高精度数据,可以任意长,变量要以字母h 结尾,以向Beet! 表明这是高精度数据,对应了Java 的BigDecimal 。在Beet! 中, 任何与高精度数据进行算数运算后的结果也必然转为BigDecimal 。变量map 和list 有点像JavaScript 中的JSON , 在Beetl 中,分别是通过HashMap 和Array List
来实现的。

3、共享变量

类似全局变量,可以在任何模板中使用,需要通过group Template 的API 添加。可通过groupTemplate.setSharedVars(Map<String, Object> sharedVars)传入变量。这些变量能用在 所有模板 的任何一个地方,如:考虑为JS成一个版本号,可以在BeetlExtConfig 中添加如下代码:

@Configuration
public class BeetlExtConfig {
@Autowired GroupTemplate grouptempldate;
@PostConstruct
    public void config(){
       grouptempldate.getSharedVars().put("jsVersion",System.currentTimeMillis());
    }
}

因此,可以在任何模板中使用jsVersion 变量:

<script src="/js/xxxx.js?version=${jsVersion}"/>

例2:

GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
Map<String,Object> shared = new HashMap<String,Object>();
shared.put("name", "beetl");
gt.setSharedVars(shared);

4、模板变量

@var template ={
<br><span>${a}</span>
@ };


四、表达式

1、结算表达式

类似JS , 支持+、-、*、/、%等表达式,变量的类型与相应的Java 类型一致:

@var b = 1;
@var a= (b+12 ) *3 , c=12/5 ;
a=$ {a} , b=$ {b}

a 的值是39, c 的值是2.4 。计算中如果需要使用高精度数代替double,则需要在数字后面加上“ h”来表示这是一个高精度数据,对应了Java 的BigDecimal。

@var a =12323232323 . 232323h ;
@var b= 343434 . 00001h;
${a*b} ${a/b} < /span >

2、逻辑表达式

Beetl 支持类似JavaScript 、Java 的条件表达式,如〉、〈、二、! =、〉=、<=、!、&&和||,
以及三元表达式等,如以下例子所示。

@if(user.status==l&&user.salary<lOOO) {
<span>失业</span>
@};

三元表达式如果只考虑true 条件对应的值的话,可以做简化,以下两行效果是一样的:

@ var a = 1 ;
${a==1?"selected":''}
${a==1?"selected"}

以上三元表达式中, a 的值为1, 因此表达式返回的结果是“ selected ”,如果a 的值不是1,则返回空字符串。在简化的三元表达实例中,则自动为null, Beetl 占位符对null 值不做输出,因此效果是一样的。

五、控制语句

1、循环语句

Beetl 支持多种循环方式,最常用的是for···in 循环语句:

@for(user in userList) {
<span>${user.name}</span>
@}

上面的例子中, for 循环会从userList 中取出每一个元素赋值给user 变量,可以在for 的循环体中使用u ser 变量。userList 可以是Java 中的集合、数组。

如果遍历的是Java 中的Map ,则遍历的元素对应于Map 的En町,因此,还需要通过key 和value 来获取遍历的元素,如

@for(entry in configMap} {
@var name = entry.key, user = entry.value;
<span>${user.name}</span>
@}

for···in 循环支持elsefor 语法,即如果未进入循环体,则执行e lsefor 部分,比如

@var array=[] ;
@for(item in array} {
   ...
@elsefor{
<span>无数据<span>
@}

上面的例子中, array 变量定义为集合,因为是空集合,所以上述循环执行了eslefor 部分。

for·· ·in 支持在集合变量后加上感叹号以进行安全输出(参考4 . 10 节安全输出的内容〉,如果变量不存在或者为null,则不进入循环体,比如:

@ var data = null;
@ for(entry in data.userList!} {
@}

在上面的例子中,因为变量data 为空,所以data . userList 会报出空指针错误,可以通过在变量表达式后面加上“ !” 来进行安全输出,意思是“我知道这个变量表达式可能不存在,也可能为空,不要报错”。在for···in 循环中, 自动解释为不报错,不进入循环体。

Beetl 循环还支持其他形式的循环,比如(for(exp;exp,exp)这种,或者while(exp)这种。

@for(var i=l;i<lO;i++}{
<span>${i}</span>
@)

for·· · in 循环也可以得到循环的上下文信息,在for · ··in 循环中,会自动创建一个
变量名+ LP 后缀的变量,提供了循环的上下文信息,比如:

@for(user in users){
<span>${useLP.index}</span>
@}

其他还有:
userLP.index,当前的索引,从1 开始;
userLP .size, 集合的长度;
userLP .first, 是否是第一个;
userLP. last, 是否是最后一个;
userLP. even, 索引是否是偶数;
userLP.odd,索引是否是奇数。

2、条件语句

Beetl的条件语句有if else ,同Java 和JS 的用法一样,还提供了switc h case 这样的语法,不同的是switc h case 支持任何类型的表达式。

@var a=true,b=l;
@if (a&&b==l) (
<span> .. . .. . </span>
@}else if (a)(
<span> ...... </span>
@} else{
<span> </span>
@}

同时Beetl 也支持switch case 的加强版sel ect -case ,允许case 中有逻辑表达式,同时,也不
需要每个case 都break 一下,默认遇到符合条件的case 执行后就退出(假设占位符设定为〈%%>)。

<%
 var b = 2 ;
  select (b){
   case 0,1:
      print ("it's small int");
   case 2,3:
         print ("it's big int");
   default:
      print("erro");
  }
%>

3、try catch

模板渲染很少会遇到异常情况,但有些模板的高级应用仍然需要捕获异常, Beetl也支持catch 结果来捕获异常:

<%
try {
callOtherSystemView ();
}catch(error) {
print("暂时无数据") ;
%>

error 代表了一个异常,可以通过e汀or.message 来获取可能的错误信息。

六、函数调用

Beetl内直了大量的常用函数以辅助模板渲染、规则引擎等功能, 函数调用方法与JS 一样。当然也可以注册自定义的函数,自定义函数可以通过Java 来实现,或者通过Beetl 本身来实现。Beetl 提供的常用函数如下:

函数说明
print 和println输出对象,如果对象为空,则不输出。
has判断是否具有这个全局变量,如if(has(userList)) { . … } ,判断后台是否提供了user List 变量。
isEmpty判断变量或者表达式是否为空,如果不存在,为null ,都返回true :同时如果字符串空,集合空也返回true; isNotEmpty 则相反
debug在控制台打印变量或者表达式。此方法还会附带此调用所在的文件和模板,以及变量的名字等信息,方便追踪。
date日期函数,获得当前日期。
trim截取一个日期或者数字类型井返回字符串,如位im(126. l 8 , l )返回字符串126.18,位im(date(),'Y)γy’) 返回年份2017 。
parseInt 、parseLong 、parseDouble将字符串或者number 转为对应的类型
global返回一个全局变量值,参数是一个字符串,如var user = global(” user_"+i )
cookie返回指定的cookie 对象, 如var userCook = cookie(”user”),allCookies = cookie () 。
strutil.*系列函数,对字符串的操作,如strutil.split(” abc,def ’,",")组,具体参考Beetl使用手册。
array. *集合的相关函数,如array.contain
shiro.*Shiro 是常用的安全框架,并非是Beet! 的内置函数(参考官网手册)
spring.*Spring 框架中可以使用的一系列函数,比如spel 表达式。
reg . *正则表达式的相关函数

Beetl中的函数,以及后面要讲的标签函数,都允许加上类似命名空间的命名规范;如strutil.split , 这个整体是函数的名字, 这样的好处是便于管理庞大的函数。

七、格式化函数

允许在占位符输出的时候指定格式化函数来格式化输出, 最常见的有日期格式化和数字格式化。这两种格式化与Java 的SimpleDateForamt 和NumberFormat 一致。

格式化的格式是$ {exp , formatName=”可选参数”},比如格式化一个日期, 可以用:

${date(),dateFormat= "yyyy-MM-dd HH:mm"}
$ {date(),dateFormat}
$ {date (),"yyyy-MM-dd"}

数字格式化函数是numberFormat , 使用方式同dateFormat , 格式化方式与java. text.DecimalFormat一致。

八、标签函数

所谓标签函数, 即允许处理模板文件中的一块内容,功能等同于jsp tag 。比如Beetl 内置的layout 标签index .html , 作为一个页面首页:

@layout("/inc/layout.html",{title:'主题'}) {
<span>Hello , this is main part</span>
@}

layout 是一个布局的标签函数,它会将标签体的{} 部分的内容植染出来后,传给layout 指定的模板页面,其变量名默认为layoutContent ,同时也传递了title 变量。

layout.html 是布局页面:

<title>${title}</title>
<div>
${layoutContent}
</div>

常用的标签函数还有include ,用来包含一个模板页面。

九、HTML标签

HTML 是一种特殊的标签函数, Beetl在形式上与HTML 标签类似, Beetl默认通过#符号来区分Beet!l的HTML 标签。

<#footer style="simple"/>
<#richeditor id ="rid" path="${ctxPath}/upload"  name ="rname"
maxlength= " ${maxlength }与${ html }   …其他模板内容
</#richdeitor >
<#html:input id= 'aaaa'/>

如上面的例子所示,<#表示这是一个Beetl的HTML 标签,标签名字是footer ,标签属性是style 。为了实现这个footer 标签,通常有两种实现方法, 一种是用Java 写一个标签函数,注册为footer 的名字,我们将在高级部分讲述:另外一种是用Beetl语言本身实现,需要在templates/htmltag 目录下创建一个footer.tag 的文件,内容就是普通的模板内容。

@ if (style==’ simple ’){
请联系我们 ${session.user.name}
@}else{
  请联系我们${session.user.name} , phone:${session.user.phone}
@ }

style 是标签中定义的变量,因此可以在HTML 标签文件中使用。
以下是HTML 标签函数的一些使用规则:
可以在自定义标签中引用标签体的内容,标签体可以是普通文本、Beetl模板,以及嵌套的自定义标签等。上面的<# richeditor>标签体中,可用tagBody 变量来引用。
HTML 自定义标签的属性值均为字符串,如<# input value=“ 123 ”/〉,在input.tag 文件中变量value 的类型是字符串。
可以在属性标签中引用Beet! 变量,如<# input val ue=“$ {user. age }”/〉,此时在input.tag中,value 的类型取决于user.age 。

在属性中引用Beetl 变量,不支持格式化,如<#input value=“$ {user. date, 'Y)仍哺1M-dd ’}” /> ,
如果需要格式化,需要在input.tag 文件中自行格式化。
在标签属性中传JSON 变量需要谨慎,因为JSON 包含了“}”,容易与占位符混合导致解析出错,因此得使用叭”符号,如<#input value=“$ { { age:25} }”/〉。
html tag 属性名将作为其对应模板的变量名,因此请确保命名符合变量名规范。

十、安全输出

​ 模板语言和Java 语言有一个很大的不同在于模板语言使用的全局变量有可能不存在,比如对应一个编辑用户的页面,修改功能有一个user 对象,而新增功能,对于同样的这个模板, user并不存在。Beet! 在变量表达式后面使用符号“!”来提醒Beetl 此变量可能不存在,表达式将返回“ ! ”后的表达式值,如果其后没有表达式,则返回null 。

@ var user =null;
${user.wife.name!}
${user.wife.name !“单身汉” }

对于第一个表达式,使用了安全输出符号,因此当user 为null 时, 或者user 的wife 属性为null 时,其表达式返回值都是null , 第二个则返回表达式后面的值,其值可以是常量或者表达式。

注意: Beetl 提供了has 函数来判断全局变量是否存在,也可以使用isEmpty 函数来判断
全局变量是否不存在。
@if (has (user)) {

@}

(本节结束)

 类似资料: