Freemarker语法
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
特点:
1. 轻量级模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中
2. 能生成各种文本,如html,xml,java,等
3. 入门简单,它是用java编写的,很多语法和java相似
freemarker模板文件(*.ftl)的基本组成部分:
1. 文本:直接输出的内容部分
2. 注释:会被FreeMarker直接忽略, 更不会在输出内容中显示,格式为<#-- 注释内容 -->
3. 取值(插值):这部分的输出会被计算的值来替换,格式为 ${数据模型} 或 #{数据模型}(这种风格已经不建议使用)
4. ftl指令:Freemarker指令,类似于HTML标签,但是它们却是给FreeMarker的指示, 而且不会打印在输出内容中( 至于什么是内建指令,什么是自定义指令 下面会叙述到)
-
- 内建指令:开始标签:<#directivename parameter> 结束标签:</#directivename> 空标签:<#directivename parameter/>
- 自定义指令:开始标签:<@directivename parameter> 结束标签:</@directivename> 空标签:<@directivename parameter/>
文本, 插值, FTL 标签, 注释
<html> <head> <title>Welcome!</title> </head> <body> <#-- Greet the user with his/her name --> <h1>Welcome ${user}!</h1> <p>We have these animals: <ul> <#list animals as animal> <li>${animal.name} for ${animal.price} Euros </#list> </ul> </body> </html>
注意:
- FTL是区分大小写的。
list
是指令的名称而List
就不是。类似地${name}
和${Name}
或${NAME}
也是不同的。 - 请注意非常重要的一点: 插值 仅仅可以在 文本 中使用。
- FTL 标签 不可以在其他 FTL 标签 和 插值中使用。比如, 这样做是 错误 的:
<#if <#include 'foo'>='bar'>...</#if>
- 注释 可以放在 FTL 标签 和 插值中。
<h1>Welcome ${user <#-- The name of user -->}!</h1> <p>We have these animals: <ul> <#list <#-- some comment... --> animals as <#-- again... --> animal>
工作原理:(借用网上的图片)
语法及使用方法:
数据类型
和java不同,FreeMarker不需要定义变量的类型,直接赋值即可。
字符串: value = "xxxx" 。如果有特殊字符 string = r"xxxx" 。单引号和双引号是一样的。
数值:value = 1.2。数值可以直接等于,但是不能用科学计数法。
布尔值:true or false。
List集合:list = [1,2,3] ; list=[1..100] 表示 1 到 100 的集合,反之亦然。
Map集合:map = {"key" : "value" , "key2" : "value2"},key 必须是字符串哦!
实体类:和EL表达式差不多,直接点出来。
字符串操作
字符串连接:可以直接嵌套${"hello , ${name}"} ; 也可以用加号${"hello , " + name}
字符串截取:string[index]。index 可以是一个值,也可以是形如 0..2 表示下标从0开始,到下标为2结束。一共是三个数。
比较运算符
== (等于),!= (不等于),gt(大于),gte(大于或者等于),lt(小于),lte(小于或者等于)。不建议用 >,< 可能会报错!
一般和 if 配合使用
变量空判断
! 指定丢失变量的默认值;一般配置变量输出使用。 对表达式自动检查 null 值和空字符串
Invoice Date: ${(customer.invoice.date)!} //如果 customer、invoice 或者 date 中有一个为空值或空字符串,你只会得到标签: Invoice Date: Invoice Date: ${(customer.invoice.date)!'No Invoice Available'} //如果所有值丢失,你会得到: Invoice Date: No Invoice Available
?? 判断变量是否存在。一般配合if使用 <#if value??></#if>
支持 JSON
FreeMarker 内置 JSON 支持。 比方说你有以下的 JSON 存储到变量命名 user 的字符串中。
{ 'firstName': 'John', 'lastName': 'Smith', 'age': 25, 'address': { 'streetAddress': '21 2nd Street', 'city': 'New York', 'state': 'NY', 'postalCode': 10021 }}
使用 ?eval 将从字符串转换为一个 JSON 对象,然后像其他数据一样在表达式中使用。
<#assign user = user?eval>
User: ${user.firstName}, ${user.address.city}
取值(插值)指令及适用类型:
${var} 适用类型:java中常用的八大基本类型以及我们的String引用类型,但是,freemarker中boolean类型显示时true==yes false==no
//在后台文件中定义变量 String strVar = "世界你好"; int intVar = 10; boolean booVar = true; //在页面中获取变量: <font color="red"> ${strVar} </font><br> //String获取 <font color="red"> ${intVar} </font><br> //int获取 <font color="red"> ${booVar?string("yes","no")} </font> //boolean获取 //展示结果:String获取:世界你好 int获取:10 boolean获取:yes
${var!} 适用类型:对 null 或者不存在的对象进行取值,可以设置默认值,例:${var!'我是默认值'} 即,有值时显示正常值,无值时显示默认值
//在后台文件中定义变量 String strVar = "世界你好"; String str = null; //在页面中获取变量: <font color="red"> ${strVar!"我是空"} </font><br> //String获取 <font color="red"> ${str!} </font><br>//str获取 <font color="red"> ${str!"默认"} </font><br>//str获取 //展示结果:String获取:世界你好 str获取: str获取:默认
${封装对象.属性} 适用类型:对封装对象进行取值,例:${User.name}
//在后台文件中封装对象User[ name, age ] String name = "姓名"; int age = 18; //在页面中获取变量: <font color="red"> ${User.name} </font><br>//name获取 <font color="red"> ${User.age} </font><br>//age获取 //展示结果:name获取:姓名 age获取:18
${date?String('yyyy-MM-dd')} 适用类型:对日期格式进行取值,要强调的是,定义Date类型的变量时,java.util.Date无法输出日期,须使用java.sql.Date
//在后台文件中定义变量 java.sql.Date date = new Date().getTime(); java.sql.Date time = new Date().getTime(); java.sql.Date datetime = new Date().getTime(); //在页面中获取变量: <font color="red"> ${date?string('yyyy-MM-dd')} </font><br>//date获取 <font color="red"> ${date?string('HH:mm:ss')} </font><br>//time获取 <font color="red"> ${date?string('yyyy-MM-dd HH:mm:ss')} </font><br>//datetime获取 //展示结果:未给出
${var?html} 适用类型:转义HTML内容
//在后台文件中封装变量Menu[ name, model ] Menu m = new Menu(); m.setName(" freemarker "); m.setModel("<font color = 'red'>我只是个菜单</font>"); //在页面中获取变量: <font color="red"> ${m.model} </font><br>//非转义获取: ${m.model?html} </font><br>//转义获取: //展示结果: //非转义获取:我只是个菜单 //转义获取:<font color = 'red'>我只是个菜单</font>
<#assign num = 100 /> 适用类型:定义变量,支持计算和赋值
//在页面中定义变量: <#assign num = 100 /> //在页面中获取变量 <font color="red"> ${num)} </font><br>// num获取: <font color="red"> ${num * 10} </font><br>//计算结果: //展示结果:num获取:100 计算结果:1000
对List集合进行取值
<#list list集合 as item>
${item} --取值
</#list>
//在后台文件中定义变量 List<String> strList = new ArrayList<String>(); strList.add("第一个值"); strList.add("第二个值"); strList.add("第三个值"); //在页面中获取变量: <#list strList as item> ${item!}<br/> //取值 </#list> //展示结果: //第一个值 //第二个值 //第三个值
对Map集合进行取值
<#list map?keys as key>
${key}:${map[key]}
</#list>
//在后台文件中定义变量 Map<String, Object> m = new HashMap<String, Object>(); m.put("name","姓名"); m.put("age",18); m.put("sex","男"); //在页面中获取变量: <#list m?keys as key> ${key}:${m[key]} </#list> // 展示结果: //name:姓名 //age:18 //sex:男
条件判断指令:
<#if 条件>输出</#if>
//在页面中定义变量并判断条件: <#assign age = 18 /><br> <#if age == 18> <font color="red"> age = 18</font> </#if> //展示结果: age = 18
<#if 条件> 输出 <#else> 输出 </#if>
//在页面中定义变量并判断条件: <#assign age = 20 /><br> <#if age == 18> <font color="red"> age = 18</font> <#else> <font color="red"> age != 18</font> </#if> //展示结果: age != 18
<#if 条件1> 输出 <#elseif 条件2> 输出 <#else> 输出 </#if>
//在页面中定义变量并判断条件: <#assign age = 20 /><br> <#if age > 18> <font color="red">青年</font> <#elseif age == 18> <font color="red"> 成年</font> <#else> <font color="red"> 少年</font> </#if> //展示结果:成年
switch --常与case break default一起使用 参数可为字符串
//在页面中定义变量并判断: <#switch var="星期一"> <#case "星期一"> 油焖大虾 <#break> <#case "星期二"> 炸酱面 <#break> <#default> 肯德基 </#switch> //展示结果: 油焖大虾
自定义函数、自定义指令:
(1) 自定义函数 —— 实现TemplateMthodModelEx
(2) 自定义指令 —— 实现TemplateDirectiveModel
<@自定义指令名称 入参(key-value格式) ; 出参(list格式)>
运行条件
</@自定义指令名称>
注意,不同的返回值用逗号( , )间隔开
<@mydirective parameters>...</@mydirective>//对于用户自定义的指令,如果指令没有嵌套内容,那么必须这么使用 <@mydirective parameters />
常用内建函数、macro(宏指令)、function(函数指令):
(1) 常用内建函数
处理字符串 | |
---|---|
html |
对字符串进行HTML编码 |
substring |
截取字符串,包头不包尾(下标) |
cap_first |
第一个字母大写 |
lower_case |
将字符串转成小写 |
upper_case |
将字符串转成大写 |
end_with |
以什么字母结尾 |
contains |
是否包含目标字符串 |
date datetime time |
转换成日期格式 |
starts_with |
以什么字母开头 |
index_of |
返回某个指定的字符串值在字符串中首次出现的位置(下标) |
last_index_of |
获取指定字符出现的最后位置(下标) |
split |
分隔 |
trim |
去两端空格 |
size |
获得集合中元素的个数 |
int |
取得数字的整数部分 |
处理数字 | |
string x?string("0.##") |
变成小数点后几位 |
round |
四舍五入 |
floor |
去掉小数点 |
ceiling |
近1 变成整数 |
处理list | |
first |
取List值第一个值 |
last |
取List值最后一个值 |
seq_contains |
是否包含指定字符 |
seq_index_of |
指定字符所在位置 |
size |
集合大小 |
reverse |
集合倒序排列 |
sort |
对集合进行排序 |
sort_by |
根据某一个属性排序 |
chunk |
分块处理 |
其他 | |
is_string: |
是否为字符类型 |
is_number: |
是否为整数类型 |
is_method: |
是否为方法 |
(): |
判断整个变量 |
has_content: |
判断对象是否为空或不存在 |
eval: |
求值 |
(2) macro(宏指令)
可以理解为java的封装方法,供其他地方使用。宏指令也称为自定义指令,macro指令
调用:<@macro_name param />
语法:<#macro 变量名 参数>
<#nested/>
</#macro>
使用:<@变量名 />
(3) function(函数指令)
调用:${function_name(param)}
语法:<#function 变量名 参数>
<#return>
</#function>
FreeMarker 程序
通过模拟简单的代码自动生产工具来感受第一个FreeMarker程序。
项目目录结构:
项目创建流程:
第一步:创建一个maven项目导入 FreeMarker jar 包
第二步:创建目录templates,并创建一个 FreeMarker模版文件 hello.ftl
第三步:创建一个运行FreeMarker模版引擎的 FreeMarkerDemo.java 文件
第四步:运行main方法后刷新项目
pom.xml 文件 ,maven 项目核心文件,管理 jar 包。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.freemark</groupId>
<artifactId>freemarkerStudy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
</dependencies>
</project>
hello.ftl FreeMarker基本语法: ${xxx} xxx 相当于占位符,java后台给xxx赋值后,再通过${}输出
package ${classPath}; public class ${className} { public static void main(String[] args) { System.out.println("${helloWorld}"); } }
FreeMarkerDemo.java 核心方法,使用 FreeMarker 模版引擎。
package com.freemark.hello; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; /** * 最常见的问题: * java.io.FileNotFoundException: xxx does not exist. 解决方法:要有耐心 * FreeMarker jar 最新的版本(2.3.23)提示 Configuration 方法被弃用 * 代码自动生产基本原理: * 数据填充 freeMarker 占位符 */ public class FreemarkerDemo { private static final String TEMPLATE_PATH = "src/main/java/com/freemark/hello/templates"; private static final String CLASS_PATH = "src/main/java/com/freemark/hello"; public static void main(String[] args) { // step1 创建freeMarker配置实例 Configuration configuration = new Configuration(); Writer out = null; try { // step2 获取模版路径 configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH)); // step3 创建数据模型 Map<String, Object> dataMap = new HashMap<String, Object>(); dataMap.put("classPath", "com.freemark.hello"); dataMap.put("className", "AutoCodeDemo"); dataMap.put("helloWorld", "通过简单的 <代码自动生产程序> 演示 FreeMarker的HelloWorld!"); // step4 加载模版文件 Template template = configuration.getTemplate("hello.ftl"); // step5 生成数据 File docFile = new File(CLASS_PATH + "\\" + "AutoCodeDemo.java"); out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile))); // step6 输出文件 template.process(dataMap, out); System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^AutoCodeDemo.java 文件创建成功 !"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != out) { out.flush(); } } catch (Exception e2) { e2.printStackTrace(); } } } }
运行程序后刷新项目,会发现多了java类,xml。通过FreeMarker做了一个简易的工具类,公司的一个标准管理页面及其增删改查等功能,以及相关的配置文件(十三个文件),一个回车就全部自动生成(偷懒ing)。
FreeMarker Web
这里是和SpringMVC整合的,SpringMVC的配置就不多说了,源码地址:https://gitee.com/itdragon/springmvc
//导入相关的jar pom.xml <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.20</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.1.4.RELEASE</version> </dependency> //springmvc的配置文件 <!-- 整合Freemarker --> <!-- 放在InternalResourceViewResolver的前面,优先找freemarker --> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/views/templates"/> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="prefix" value=""/> <property name="suffix" value=".ftl"/> <property name="contentType" value="text/html; charset=UTF-8"/> </bean> //Controller 层 import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HelloFreeMarkerController { @RequestMapping("/helloFreeMarker") public String helloFreeMarker(Model model) { model.addAttribute("name","ITDragon博客"); return "helloFreeMarker"; } } //Freemarker文件 <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>FreeMarker Web</title> </head> <body> <h1>Hello ${name} !</h1> </body> </html>
小结
-
知道了FreeMarker是一块模版引擎,可以生产xml,html,java等文件
-
知道了FreeMarker文件提供占位符,java文件提供数据,通过FreeMarker模版引擎生产有数据的页面,文中将数据放在Map中。web应用可以用setter/getter 方法
-
知道了FreeMarker语法中字符串的显示特殊字符,截取的操作。以及一些内置方法的使用
-
重点了解FreeMarker的空判断知识点。判断变量是否为空用 "??" ,如果变量为空设置默认值。如果不注意空问题,可能会出现黄色页面的提示哦!
-
FreeMarker的宏概念,命名空间,引入文件,给变量赋值,集合的遍历等。
-
Freemarker 整合SpringMVC。