Beetl
一、简介
Beetl和JS语法很像,也能支持html 标签,引擎性能5-6倍与freemaker,2倍于JSP,使得开发CMS系统做静态化页面性价比高
二、语法
1、定界符与占位符号
定界符:<% %>: 写一些 for循环 if判断 变量赋值 等操作
占位符:${ }:占位符用于静态文本里嵌入占位符用于输出
如下是正确例子
<%
var a = 2;
var b = 3;
var result = a+b;
%>
hello 2+3=${result}
注意点:千万不要在定界符里使用占位符号,因为占位符仅仅嵌在静态文本里,如下例子是错误例子
<%
var a = "hi";
var c = ${a}+"beetl"; //应该是var c = a+"beetl"
%>
2、注释
单行注释采用// 注释内容
多行注视采用/*注释内容*/
3、临时变量定义
在模板中定义的变量成为临时变量,这类似js中采用var 定义的变量,如下例子
<%
var a = 3;
var b = 3,c = "abc",d=true,e=null;
var f = [1,2,3];
var g = {key1:a,key2:c};
var i = a+b;
%>
4、全局变量定义
全局变量是通过template.binding传入的变量,这些变量能在模板的任何一个地方,包括子模板都能访问到
//此处是java代码,给模板赋值
template.binding("list",service.getUserList());
//在模板里
<%
for(user in list){
%>
用户名称:,${user.name};
<% } %>
因为我们使用的是3.0版本所以,自从2.8.0版本后,有一个特殊的变量成为root变量,当模板找不到变量的时候,会寻找root变量的属性来作为变量的值,这个root变量必须绑定为"_root"
//此处是java代码,给模板赋值
template.binding("_root",new User());
//在模板里
${name}
${wife.name}
这里name 和 wife都是User对象的属性
5、共享变量
共享变量指在所有模板中都可以引用的变量,可通过groupTemplate.setSharedVars(Map<String, Object> sharedVars)传入变量,这些变量能用在 所有模板 的任何一个地方
@Test
void mapVariableFile() throws IOException, ParseException {
//模板目录
String root = "D:\\wyx\\beetl\\template";
//文件模板加载器
FileResourceLoader resourceLoader = new FileResourceLoader(root);
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
// 设置共享变量
Map<String,Object> shared = new HashMap<String,Object>();
shared.put("share", "我是beetl共享变量");
gt.setSharedVars(shared);
Template t = gt.getTemplate("/shareOneTemplate.html");
t.binding("shareTitleOne","我是共享变量模板一");
String str = t.render();
logger.info("共享一输出结果:\n" + str);
t = gt.getTemplate("/shareTwoTemplate.html");
t.binding("shareTitleTwo","我是共享变量模板二");
str = t.render();
logger.info("共享二输出结果:\n" + str);
//t1.txt
hi,${name}
//t2.txt
hello,${name}
6、引用属性
①Beetl支持通过”.”号来访问对象的的属性,如果javascript一样。如果User对象有个getName()方法,那么在模板中,可以通过$ {xxx.name}来访问
②如果模板变量是数组或者List类,这可以通过[] 来访问,如$ {userList[0]}
③如果模板变量是Map类,这可以通过[]来访问,如$ {map[“name”]},如果key值是字符串类型,也可以使用$ {map.name}.但不建议这么使用,因为会让模板阅读者误以为是一个Pojo对象
④Beetl也支持Generic Get方式,即如果对象有一个public Object get(String key)方法,可以通过”.”号或者[]来访问,譬如 $ {activityRecord.name}或者$ {activityRecord[“name”] }都将调用activityRecord的 get(String key)方法。如果对象既有具体属性,又有Generic get(这种模型设计方式是不值得鼓励),则以具体属性优先级高.
⑤Beetl也可以通过[]来引用属性,如$ {user[“name”]} 相当于$ {user.name}.这跟javascript保持一致。但建议不这么做,因为容易让阅读模板的人误认为这是一个Map类型
⑥Beetl 还可以定义额外的对象属性,而无需更改java对象,这叫着虚拟属性,如,对于所有集合,数组,都有共同的虚拟属性size.虚拟属性是“.~”+虚拟属性名
template.binding("list",service.getUserList());
template.binding("pageMap",service.getPage());
//在模板里
总共 ${list.~size}
<%
for(user in list){
%>
hello,${user.name};
<% } %>
当前页${pageMap['page']},总共${pageMap["total"]}
7、算数表达式
Beetl支持类似javascript的算术表达式和条件表达式,如+ - * / % 以及(),以及自增++,自减–
<%
var a = 1;
var b = "hi";
var c = a++;
var d = a+100.232;
var e = (d+12)*a;
var f = 122228833330322.1112h //h以表示这是长精度BigDecimal
%>
8、逻辑表达式
Beetl支持类似Javascript,java的条件表达式 如>,<,==,!=,>= , <= 以及 !, 还有&&和 || ,还有三元表达式等,如下例子
<%
var a = 1;
var b="good";
var c = null;
<%
var a = 1 ;
%>
${a==1?"ok":''}
${a==1?"ok"}
%>
9、循环语句
Beetl支持丰富的循环方式,如for-in,for(exp;exp;exp),以及while循环,以及循环控制语句break;continue; 另外,如果没有进入for循环体,还可以执行elsefor指定的语句。
for-in
<%
for(user in userList){
print(userLP.index);//userLP.index *当前的索引,从1开始
print(user.name);
}
%>
*userLP.index *当前的索引,从1开始
userLP.dataIndex 索引,从0开始
*userLP.size *集合的长度
userLP.first 是否是第一个
userLP.last 是否是最后一个
userLP.even 索引是否是偶数
userLP.odd 索引是否是奇数
for(exp;exp;exp)
对于渲染逻辑更为常见的是经典的for循环语句,如下例子
<%
var a = [1,2,3];
for(var i=0;i<a.~size;i++){
print(a[i]);
}
%>
10、条件语句
if else
同js一样,支持if else,如下例子
<%
var a =true;
var b = 1;
if(a&&b==1){
}else if(a){
}else{
}
%>
11、try-catch
通常模板渲染逻辑很少用到try-catch 但考虑到渲染逻辑复杂性,以及模板也有不可控的地方,所以提供try catch,在渲染失败的时候仍然能保证输出正常
<%
try{
callOtherSystemView()
}catch(error){
print("暂时无数据");
}
%>
error代表了一个异常,你可以通过error.message 来获取可能的错误信息
也可以省略catch部分,这样出现异常,不做任何操作
<%
var date = date();
var len = strutil.length("cbd");
println("len="+len);
%>
12、函数调用
Beetl内置了少量实用函数,可以在Beetl任何地方调用。如下例子是调用date 函数,不传参数情况下,返回当前日期
Beetl内置函数请参考附录,以下列出了常用的函数
①date 返回一个java.util.Date类型的变量,如 date() 返回一个当前时间(对应java的java.util.Date); ${date( “2011-1-1” , “yyyy-MM-dd” )} 返回指定日期,date(ms),指定一个毫秒数。相当于调用java.util.Date(ms)
②print 打印一个对象 print(user.name);
③println 打印一个对象以及回车换行符号,回车换号符号使用的是模板本身的,而不是本地系统的.如果仅仅打印一个换行符,则直接调用println() 即可
④nvl 函数nvl,如果对象为null,则返回第二个参数,否则,返回自己 nvl(user,“不存在”)
⑤isEmpty 判断变量或者表达式是否为空,变量不存在,变量为null,变量是空字符串,变量是空集合,变量是空数组,此函数都将返回true
⑥isNotEmpty 同上,判断对象是否不为空
⑦has 变量名为参数,判断是否存在此"全局变量",如 has(userList),类似于1.x版本的exist(“userList”),但不需要输入引号了.注意,has和isEmpety 判断的是从java传到模板的全局变量,而不是临时变量
⑧hasAttrbiute 测试目标对象是否有此属性,hasAttribute(user,“name”)
⑨assert 如果表达式为false,则抛出异常
trim 截取数字或者日期,返回字符,如trim(12.456,2)返回"12.45",trim(date,‘yyyy’)返回"2017"
⑩trunc 截取数字,保留指定的小数位,如trunc(12.456,2) 输出是12.45.不推荐使用,因为处理float有问题,兼容原因保留了
①②、decode 一个简化的if else 结构,如 decode(a,1,“a=1”,2,“a=2”,“不知道了”),如果a是1,这decode输出"a=1",如果a是2,则输出"a==2", 如果是其他值,则输出"不知道了"
①③debug 在控制台输出debug指定的对象以及所在模板文件以及模板中的行数,如debug(1),则输出1 [在3行@/org/beetl/core/lab/hello.txt],也可以输出多个,如debug(“hi”,a),则输出hi,a=123,[在3行@/org/beetl/core/lab/hello.txt]
①④parseInt 将数字或者字符解析为整形 如 parseInt(“123”);
①⑤parseLong 将数字或者字符解析为长整形,parseInt(123.12);
①⑥parseDouble 将数字或者字符解析为浮点类型 如①⑦parseDouble(“1.23”)
①⑧range 接收三个参数,初始值,结束值,还有步增(可以不需要,则默认为1),返回一个Iterator,常用于循环中,如for(var i in range(1,5)) {print(i)},将依次打印1234.
①⑨flush 强制io输出。
②〇json,将对象转成json字符串,如 var data = json(userList) 可以跟一个序列化规则 如,var data = json(userList,"[*].id:i"),具体参考 https://git.oschina.net/xiandafu/beetl-json
②①pageCtx ,仅仅在web开发中,设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx(“title”,“用户添加页面”),在其后任何地方,可以pageCtx(“title”) 获取该变量
②②type.new 创建一个对象实例,如 var user = type.new(“com.xx.User”); 如果配置了IMPORT_PACKAGE,则可以省略包名,type.new(“User”)
②③type.name 返回一个实例的名字,var userClassName = type.name(user),返回"User"
②④global 返回一个全局变量值,参数是一个字符串,如 var user = global(“user_”+i);
②⑤cookie 返回指定的cookie对象 ,如var userCook = ②⑥cookie(“user”),allCookies = cookie();
13、安全输出
安全输出是任何一个模板引擎必须重视的问题,否则,将极大困扰模板开发者。Beetl中,如果要输出的模板变量为null,则beetl将不做输出,这点不同于JSP,JSP输出null,也不同于Freemarker,如果没有用!,它会报错.
模板中还有俩种情况会导致模板输出异常
有时候模板变量并不存在(譬如子模板里)
模板变量为null,但输出的是此变量的一个属性,如${user.wife.name}
针对前俩种情况,可以在变量引用后加上!以提醒beetl这是一个安全输出的变量。
如${user.wife.name! },即使user不存在,或者user为null,或者user.wife为null,或者user.wife.name为null beetl都不将输出
14、格式化
<% var date = date(); %>
Today is ${date,dateFormat="yyyy-MM-dd"}.
Today is ${date,dateFormat}
salary is ${salary,numberFormat="##.##"}
//简写
${date,“yyyy-MM-dd”}
15、标签函数
所谓标签函数,即允许处理模板文件里的一块内容,功能等于同jsp tag。如Beetl内置的layout标签
index.html
<%
//变量title来自于layout标签函数的参数
layout("/inc/layout.html",{title:'主题'}){
%>
Hello,this is main part
<% } %>
layout.html
title is ${title}
body content ${layoutContent}
footer
三、使用
整合springboot
pom文件
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl-framework-starter</artifactId>
<version>1.2.28.RELEASE</version>
</dependency>
yml文件
把Beetl的btl后缀改为html后缀文件
beetl:
suffix: html
模板基础配置
从GroupTemplate开始
Beetl的核心是GroupTemplate,创建GroupTemplate需要俩个参数,一个是模板资源加载器,一个是配置类
①、字符串模板加载器
//字符串模板加载器
StringTemplateResourceLoader resourceLoader = new StringTemplateResourceLoader();
//模板基础配置
Configuration cfg = Configuration.defaultConfiguration();
//Beetl的核心是GroupTemplate,创建GroupTemplate需要俩个参数,一个是模板资源加载器,一个是配置类
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
//获取模板
Template t = gt.getTemplate("hello,${name},${title}");
//渲染数据
t.binding("name", "我是名称");
t.binding("title", "我是标题");
//渲染结果
String str = t.render();
logger.info("输出结果:\n" + str);
②文件资源模板加载器(多变量赋值)
//模板文件目录
String root = "D:\\wyx\\beetl\\template";
//文件模板加载器
FileResourceLoader resourceLoader = new FileResourceLoader(root);
//配置类
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
//获取模板
Template t = gt.getTemplate("/muchVariableTemplate.html");
//渲染数据
t.binding("title", "beetl多变量变量页面");
t.binding("name", "beetl");
t.binding("age", 18);
t.binding("time", new Date());
String str = t.render();
logger.info("输出结果:\n" + str);
③文件资源模板加载器(map类型变量赋值)
//模板文件目录
String root = "D:\\wyx\\beetl\\template";
//文件模板加载器
FileResourceLoader resourceLoader = new FileResourceLoader(root);
//配置类
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
//获取模板文件
Template t = gt.getTemplate("/muchVariableTemplate.html");
//组装模板数据
Map<String, Object> map = new HashMap<>();
map.put("title", "beetl map变量页面");
map.put("name", "我是map");
t.binding("age", 20);
t.binding("time", new SimpleDateFormat("yyyy-MM-dd").parse("2021-01-01"));
//模板赋值
t.binding(map);
String str = t.render();
logger.info("输出结果:\n" + str);
④共享变量 并生成html页面
//模板目录
String root = "D:\\wyx\\beetl\\template";
//文件模板加载器
FileResourceLoader resourceLoader = new FileResourceLoader(root);
Configuration cfg = Configuration.defaultConfiguration();
GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
// 设置共享变量
Map<String,Object> shared = new HashMap<String,Object>();
shared.put("share", "我是beetl共享变量");
gt.setSharedVars(shared);
//获取模板一
Template t = gt.getTemplate("/shareOneTemplate.html");
//渲染模板一自己的数据
t.binding("shareTitleOne","我是共享变量模板一");
//创建生成的html页面路径
File fileOne = new File("D:\\wyx\\beetl\\page\\shareOnePage.html");
if(!fileOne.exists()) {
fileOne.createNewFile();
}
Writer writer = new FileWriter(fileOne);
//将渲染结果写入html中
t.renderTo(writer);
//获取模板二
t = gt.getTemplate("/shareTwoTemplate.html");
t.binding("shareTitleTwo","我是共享变量模板二");
File fileTwo = new File("D:\\wyx\\beetl\\page\\shareTwoPage.html");
if(!fileTwo.exists()) {
fileTwo.createNewFile();
}
Writer writerTwo = new FileWriter(fileTwo);
t.renderTo(writerTwo);
logger.info("生成页面成功-----");
FTP 文件传输
作用:将生成的html代码传到服务器上
pom文件
<!-- ftp文件上传-->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.3</version>
</dependency>
public static void main(String[] args) {
String htmlUrl = "D:\\wyx\\beetl\\page\\a.html";
ftpUploadFile(htmlUrl);
}
/**
*
* @param htmlUrl : 文件地址
*/
public static void ftpUploadFile(String htmlUrl){
//创建客户端对象
FTPClient ftp = new FTPClient();
InputStream local=null;
try {
//连接ftp服务器
ftp.connect("47.110.152.159", 21);
//登录
ftp.login("wyxftp", "wei123..");
//设置上传路径
String path="/home/wyxftp/a";
//检查上传路径是否存在 如果不存在返回false
boolean flag = ftp.changeWorkingDirectory(path);
if(!flag){
//创建上传的路径 该方法只能创建一级目录,在这里如果/home/ftpuser存在则可创建image
ftp.makeDirectory(path);
}
//指定上传路径
ftp.changeWorkingDirectory(path);
//指定上传文件的类型 二进制文件
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//读取本地文件
File file = new File(htmlUrl);
local = new FileInputStream(file);
//第一个参数是文件名
ftp.storeFile(file.getName(), local);
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭文件流
local.close();
//退出
ftp.logout();
//断开连接
ftp.disconnect();
logger.info("上传成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
FastDFS 文件存储
作用:存储文件,返回地址,根据地址获取文件
pom文件依赖
<!-- fastdfs -->
<dependency>
<groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId>
<version>1.26.7</version>
</dependency>
yml文件
fdfs:
so-timeout: 2500 # 读取时间
connect-timeout: 600 # 连接超时时间
tracker-list: # tracker服务配置地址列表
- 47.110.152.159:22122
upload:
base-url: http://47.110.152.159:84/
String path = "D:\\wyx\\beetl\\page\\b.html";
File file = new File(path);
InputStream inputStream = new FileInputStream(file);
//上传到FastDFS
// 1、获取扩展名
String extension = StringUtils.substringAfterLast(file.getName(), ".");
// 2、上传
StorePath storePath = storageClient.uploadFile(inputStream, file.length(), extension, null);
// 返回路径
String fullPath = storePath.getFullPath();
logger.info("url:" + prop.getBaseUrl() + fullPath);