1. beetl的安装
- 使用maven:
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>2.9.3</version>
</dependency>
- 非maven项目:https://gitee.com/xiandafu/beetl2.0/attach_files
2. beetl的基本使用
StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader();
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
Template t = gt.getTemplate("hello,${name}");
t.binding("name", "beetl");
String str = t.render();
System.out.println(str);
先选择模板资源加载器和配置类;然后通过这两个创建GroupTemplate(建议使用单模式创建);通过GroupTemplate对象加载模板得到模板对象,就可以对模板进行一系列的操作
3. 模板资源加载器的种类
- StringTemplateResourceLoader:字符串模板加载器,用于加载字符串模板
- FileResourceLoader:文件模板加载器,需要一个根目录作为参数构造,传入getTemplate方法的String是模板文件相对于根目录的相对路径
- ClasspathResourceLoader:文件模板加载器,模板文件位于Classpath里
- WebAppResourceLoader:用于webapp集成,假定模板根目录就是WebRoot目录
- MapResourceLoader : 可以动态存入模板
- CompositeResourceLoader:混合使用多种加载方式
- 自定义资源模板加载器:有时候模板可能来自文件系统不同目录,或者模板一部分来自某个文件系统,另外一部分来自数据库,还有的情况模板可能是加密混淆的模板,此时需要自定义资源加载,继承ResouceLoader才能实现模板功能
4. 模板基础配置
Beetl建议通过配置文件配置GroupTemplate,主要考虑到IDE插件未来可能会支持Beetl模板,模板的属性,和函数等如果能通过配置文件获取,将有助于IDE插件识别。 配置GroupTemplate有俩种方法
- 配置文件: 默认配置在/org/beetl/core/beetl-default.properties 里,Beetl首先加载此配置文件,然后再加载classpath里的beetl.properties,并用后者覆盖前者。配置文件通过Configuration类加载,因此加载完成后,也可以通过此类API来修改配置信息
- 通过调用GroupTemplate提供的方法来注册函数,格式化函数,标签函数等
配置文件分为三部分,第一部分是基本配置,第二部分是资源类配置,可以在指定资源加载类,以及资源加载器的属性(这个配置在spring框架里,通过spring或者springboot的配置机制实现覆盖,并未起作用)
5. 定界符:Beetl模板语言书写区间标识,类似于JSP的<% %>,可以在开始标识和结束标识之间书写模板语言代码;可以通过基本配置进行配置自己的定界符;如果代码中使用到定界符符号,可以使用转义字符"\";定界符内不能使用占位符,而是直接使用变量;模板语言类似于js语法。
6. 占位符:类似于JSP的<%= %>表达式;在模板中直接使用变量,不能在定界符内使用;可以通过基本配置进行配置自己的占位符。
7. 注释:定界符内的注释与js语法的单行多行注释一样,都是采用// 或 /* */
8. 临时变量定义:模板中定义的变量,只能在模板内访问;var 变量名 = 值
9. 全局变量定义:能在整个模板及子模板访问的变量;只能通过java代码使用模板对象的binding方法加入;2.8版本以后加入了一个默认的root变量,当找不到变量时,会去root中查找,加入root变量需要使用binding方法加入
key='_root'
10. 共享变量:所有模板中都可以引用的变量;需要在GroupTemplate对象中调用.setSharedVars(Map<String, Object> sharedVars)传入变量
11. 模板变量:与临时变量不同的是,定义包住了用到它的模板内容
<%
var content = {
var c = "1234";
print(c);
%> 模板其他内容: <% }; %>
12. 引用变量:
- 对象:支持通过”.”号来访问对象的的属性,如果javascript一样。如果User对象有个getName()方法,那么在模板中,可以通过${xxx.name}来访问;也可以通过[]来引用属性,如${user[“name”]} 相当于${user.name},这跟javascript保持一致。但建议不这么做,因为容易让阅读模板的人误认为这是一个Map类型
- 数组:如果模板变量是数组或者List类,这可以通过[] 来访问,如${userList[0]} map:如果模板变量是Map类,这可以通过[]来访问,如${map[“name”]},如果key值是字符串类型,也可以使用${map.name}.但不建议这么使用,因为会让模板阅读者误以为是一个Pojo对象
- Generic Get方式:即如果对象有一个public Object get(String key)方法,可以通过”.”号或者[]来访问,譬如 ${activityRecord.name}或者${activityRecord[“name”] }都将调用activityRecord的 get(String key)方法。如果对象既有具体属性,又有Generic get(这种模型设计方式是不值得鼓励),则以具体属性优先级高.
- 虚拟属性:还可以定义额外的对象属性,而无需更改java对象,这叫做虚拟属性,如,对于所有集合,数组,都有共同的虚拟属性size.虚拟属性是“.~”+虚拟属性名
13. 属性赋值:与JS相同(对象、数组、map)
14. 表达式
- 算术表达式:【+】【-】【*】【/】【%】【()】【++】【--】
- 逻辑表达式:【<】【>】【==】【!=】【>=】【<=】【!】【&&】【||】【?:】(三元表达式如果只考虑true条件对应的值的话,可以省略:及后面的)
15. 循环语句
- for-in
<%
for(user in userList){
print(userLP.index);
print(user.name);
}
%>
在循环体内能使用隐含定义的变量;其命名规范是item名称后加上LP,如例子中的userLP;隐含的变量如下:
- userLP.index 当前的索引,从1开始
- userLP.size 集合的长度
- userLP.first 是否是第一个
- userLP.last 是否是最后一个
- userLP.even 索引是否是偶数
- userLP.odd 索引是否是奇数
- for(exp;exp;exp)
<%
var a = [1,2,3];
for(var i=0;i<a.~size;i++){
print(a[i]);
}
%>
- while
<%
var i = 0;
while(i<5){
print(i);
i++;
}
%>
- elsefor(表达如果循环体没有进入,则执行elsefor 后的语句)
<%
var list = [];
for(item in list){ }elsefor{
print("未有记录");
}
%>
16. 条件语句
- if-else(与JS一样)
- switch-case(与JS一样,但switch变量可以支持任何类型)
- select-case(是switch case的增强版。他允许case 里有逻辑表达式,同时,也不需要每个case都break一下,默认遇到合乎条件的case执行后就退出)
<%
var b = 1;
select(b){
case 0,1:
print("it's small int");
case 2,3:
print("it's big int");
default:
print("error");
}
%>
select 后也不需要一个变量,这样case 后的逻辑表达式将决定执行哪个case.其格式是
<%
select {
case boolExp,orBoolExp2:
doSomething();
}
%>
17. 异常捕获 try-catch(与JS一样,通过.message获得异常信息)
18. 虚拟属性:虚拟属性也是对象的属性,是虚拟的,非模型对象的真实属性,这样的好处是当模板需要额外的用于显示的属性的时候但又不想更改模型,便可以采用这种办法 如beetl内置的虚拟属性.~size 针对了数组以及集合类型
19. 函数调用:Beetl内置了少量实用函数,可以在Beetl任何地方调用;函数名支持namespace方式
- 定义beetl的方法非常容易,有三种方法(详细看官网文档高级功能部分)
- 实现Function类的call方法,并添加到配置文件里,或者显示的通过代码注册registerFunction(name,yourFunction)
- 可以直接调用registerFunctionPackage(namespace,yourJavaObject),这时候yourJavaObject里的所有public方法都将注册为Beetl方法,方法名是namespace+"."+方法名
- 可以直接写模板文件并且以html作为后缀,放到root/functions目录下,这样此模板文件自动注册为一个函数,其函数名是该模板文件名。
- Beetl内置函数
20. 安全输出
- 说明
- 如果要输出的模板变量为null,则beetl将不做输出
- 模板中有两种情况会导致模板输出异常;
- 有时候模板变量并不存在(譬如子模板里)
- 模板变量为null,但输出的是此变量的一个属性,如${user.wife.name}
- 可以在变量引用后加上!以提醒beetl这是一个安全输出的变量,如${user.wife.name! }
- 可以在!后增加一个常量(字符串,数字类型等),或者另外一个变量,方法,本地调用,作为默认输出,${user.wife.name!”单身”}
- 输出模板变量发生的任何异常,如变量内部抛出的一个异常,这需要使用格式${!(变量)},这样,在变量引用发生任何异常情况下,都不作输出;在变量后加上!不仅仅可以应用于占位符输出(但主要是应用于占位符输出),也可以用于表达式中
- 在有些模板里,可能整个模板都需要安全输出,也可能模板的部分需要安全输出,使用者不必为每一个表达式使用!,可以使用beetl的安全指示符号来完成安全输出 如:
<%
DIRECTIVE SAFE_OUTPUT_OPEN;
%>
${user.wife.name}
模板其他内容,均能安全输出……
<%
//关闭安全输出。
DIRECTIVE SAFE_OUTPUT_CLOSE;
%>
- 安全输出表达式
- 字符串常量,如 ${user.count!"无结果"}
- boolean常量 ${user.count!false}
- 数字常量,仅限于正数,因为如果是负数,则类似减号,容易误用,因此,如果需要表示负数,请用括号,如${user.count!(-1)}
- class直接调用,如${user.count!@User.DEFAULT_NUM}
- 方法调用,如 ${user.count!getDefault() }
- 属性引用,如 ${user.count!user.maxCount }
- 任何表达式,需要用括号
21.格式化
- 格式化函数只需要一个字符串作为参数放在=号后面,如果没有为格式化函数输入参数,则使用默认值,dateFormat格式化函数默认值是local,如${date,dateFormat="yyyy-MM-dd"}、${salary,numberFormat="##.##"}
- Beetl也允许为指定的java class设定格式化函数,譬如已经内置了对java.util.Date,java.sql.Date 设置了了格式化函数,因此上面的例子可以简化为${date,“yyyy-MM-dd”}
- Beetl针对日期和数字类型提供的默认的格式化函数,在org/beetl/core/beetl-default.properties里注册了
- 自定义格式化函数(详细看官网文档高级部分)
22. 标签函数(所谓标签函数,即允许处理模板文件里的一块内容,功能等于同jsp tag)
- layout("/inc/layout.html",{title:'主题'}){}
- include("/inc/header.html"){}
23. HTML标签
- 自定义html标签规则
- 可以在自定义标签里引用标签体的内容,标签体可以是普通文本,beetl模板,以及嵌套的自定义标签等
- 可以在属性标签里引用beetl变量,如<#input value="${user.age}" />
- 在属性里引用beetl变量,不支持格式化,如<#input value="${user.date,'yyyy-MM-dd'}"/>,如果需要格式化,需要在input.tag文件里自行格式化
- 在标签属性里传json变量需要谨慎,因为json包含了"}",容易与占位符混合导致解析出错,因此得使用"\"符号,如<#input value="${ {age:25\} }" />
- html tag 属性名将作为 其对应模板的变量名。如果属性名包含“-”,则将转为驼峰命名的变量,如data-name,转为dataName
- 默认机制下,HTMLTagSupportWrapper2 实现了标签(2.8.x以前使用HTMLTagSupportWrapper)
24. 绑定变量的HTML标签
25. 直接调用java方法和属性
- 可以通过符号@来表明后面表达式调用是java风格,可以调用对象的方法,属性
- GroupTemplate可以配置为不允许直接调用Class
- 可以通过安全管理器配置到底哪些类Beetl不允许调用;默认情况,java.lang.Runtime,和 java.lang.Process不允许在模板里调用
- 请按照java规范写类名和方法名,属性名。这样便于beetl识别到底调用的是哪个类,哪个方法。否则会抛出错误
- 可以省略包名,只用类名。beetl将搜索包路径找到合适的类(需要设置配置“IMPORT_PACKAGE=包名.;包名.”,包名后需要跟一个“.”, 或者调用Configuration.addPkg)方法
- 内部类(包括枚举)访问同java一样,如User类有个内部枚举类Gender,访问是User$Gender
- 表达式是java风格,但参数仍然是beetl表达式,比如 @user.sayHello(user.name).这里user.sayHello是java调用,user.name 仍然是beetl表达式
26. 严格MVC控制:如果在配置文件中设置了严格MVC,则以下语法将不在模板文件里允许,否则将报出STRICK_MVC 错误
定义变量,为变量赋值,如var a = 12是非法的
- 算术表达式 如${user.age+12}是非法的
- 除了只允许布尔以外,不允许逻辑表达式和方法调用 如if(user.gender==1)是非法的
- 方法调用,如${subString(string,1)}是非法的
- Class方法和属性调用,如${@user.getName()}是非法的
- 严格的MVC,非常有助于逻辑与视图的分离,特别当逻辑与视图是由俩个团队来完成的。如果你嗜好严格MVC,可以调用groupTemplate.enableStrict()
- 通过重载AntlrProgramBuilder,可以按照自己的方法控制到底哪些语法是不允许在模板引擎中出现的
27. 指令
- 指令格式为: DIRECTIVE 指令名 指令参数(可选) Beetl目前支持安全输出指令,分别是
- DIRECTIVE SAFE_OUTPUT_OPEN ; 打开安全输出功能,此指令后的所有表达式都具有安全输出功能,
- DIRECTIVE SAFE_OUTPUT_CLOSE ; 关闭安全输出功能。
- DIRECTIVE DYNAMIC varName1,varName2 …指示后面的变量是动态类型,Beetl应该考虑为Object. 也可以省略后面的变量名,则表示模板里所有变量都是Object
28. 类型声明
- Beetl 本质上还是强类型的模板引擎,即模板每个变量类型是特定的,在模板运行过程中,beetl 会根据全局变量自动推测出模板中各种变量和表达式类型。 也可以通过类型申明来说明beetl全局变量的类型
- 类型申明必须放到多行注释里,格式是@type( … ),里面的申明类似java方法的参数申明。正如你看到的类型申明是在注释里,也就表明了这在Beetl模板引擎中不是必须的,或者你只需要申明一部分即可
<%
/**
*@type (List<User> idList,User user)
*/
for(value in idList) .....
- 之所以提供可选的类型说明,是因为
- 提高一点性能
- 最重要的是,提高了模板的可维护性。可以让模板维护者知道变量类型,也可以让未来的ide插件根据类型声明来提供属性提示,重构等高级功能
- 要注意的是,如果在类型声明里提供的是类名,而不是类全路径,这样必须在配置文件里申明类的搜索路径((需要设置配置IMPORT_PACKAGE=包名.;包名.,或者调用Configuration.addPkg)),默认的搜索路径有java.util. 和
- java.lang.
29. 错误处理
- Beetl能较为详细的显示错误原因,包括错误行数,错误符号,错误内容附近的模板内容,以及错误原因,如果有异常,还包括异常和异常信息。 默认情况下,仅仅在控制台显示
- 默认的错误处理器仅仅像后台打印错误,并没有抛出异常,如果需要在render错误时候抛出异常到控制层,则可以使用org.beetl.core.ReThrowConsoleErrorHandler。不仅打印异常,还抛出BeetlException
- 可以自定义异常处理器,比如把错误输出到 作为渲染结果一部分输出,或者输出更美观的html内容等
- 可以在配置文件不设置异常,这样Beetl引擎将不处理异常,用户可以在外部来处理(可以在外部调用ErrorHandler子类来显示异常)
30. Beetl小工具
- BeetlKit 提供了一些便利的方法让你立刻能使用Beetl模板引擎
- public static String render(String template, Map<String, Object> paras) 渲染模板,使用paras参数,渲染结果作为字符串返回
- public static void renderTo(String template, Writer writer, Map<String, Object> paras) 渲染模板,使用paras参数
- public static void execute(String script, Map<String, Object> paras) 执行某个脚本
- public static Map execute(String script, Map<String, Object> paras, String[] locals) 执行某个脚本,将locals指定的变量名和模板执行后相应值放入到返回的Map里
- public static Map executeAndReturnRootScopeVars(String script) 执行某个脚本,返回所有*scope的所有变量和值
- public static String testTemplate(String template, String initValue) 渲染模板template,其变量来源于intValue脚本运行的结果,其所有*Scope的变量都将作为template的变量
- BeetlKit 不要用于线上系统。仅仅作为体验Beetl功能而提供的
31. 琐碎功能
- 对齐:我发现别的模板语言要是做到对齐,非常困难,使用Beetl你完全不用担心,比如velocty,stringtemlate,freemarker例子都出现了不对齐的情况,影响了美观,Beetl完全无需担心输出对齐
- Escape:可以使用\ 做escape 符号,如\$monkey\$ 将作为一个普通的文本,输出为$monkey$.再如为了在后加上美元符号(占位符恰好又是美元符号)可以用这俩种方式hello,it’s $money$\$, 或者Hello,it’s $money+"\$"$。如果要输出\符号本身,则需要用俩个\,这点与javascript,java 语义一致.
32. Web集成