FreeMarker模板基础知识

1、FreeMarker与jsp、Thymeleaf并排为三大模板引擎,用于把后端数据渲染到页面上,降低耦合度。动态数据+占位符+静态页面标签构成动态页面。
2、FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电
子邮件,配置文件,源代码等)的通用工具。 是一个Java类库。
3、FreeMarker被设计用来生成 HTML Web页面,特别是基于MVC模式的应用程序,将视图从业务逻辑中抽离出来,业务中不再包括视图的展示,而是将视图交给 FreeMarker 来输出。虽然 FreeMarker 具有一些编程的能力,但通常由 Java 程序准备要显示的数据,由 FreeMarker 生成页面,通过模板显示准备的数据。
4、FreeMarker与容器无关,因为它并不知道HTTP或Servlet。FreeMarker同样可以应用于非Web应用程序环境。我们上面的案例只是通过servlet来制造数据模型而已,在非web环境下依然可以(例:页面静态化例子)。

1、快速搭建

  • 创建Maven Web工程
  1. 导入依赖
 <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!--导入freemarker坐标依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
  1. 配置web.xml文件
 <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<!--配置模板引擎根目录 表示在webapp目录下创建以.ftl后缀的文件-->
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>DefaultEncoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
  1. 创建ftl页面
<#--freemarker隐性注释-->
<!--显性注释 浏览器检查功能能显示的注释-->
<h1>${msg}</h1>

4、创建Servlet类

/*应该先访问Servlet*/
@WebServlet("/ftl")
public class DataServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("msg","Hello word");
req.getRequestDispatcher("index.ftl").forward(req,resp);
}
}

2、数据类型

布尔类型:true false 不能展示在页面上,要转换为字符串,否则会报错。
数值类型:int float double long等 可以直接展示在页面。
日期类型:不能展示在页面上,要转为字符串格式。
字符型:可以直接展示在页面上。
sequence(序列):等价于java的数组 Array list类型。
hash:等价与java中的Map类型。
在FreeMarker模板中,如果变量值为null或者展示不存在的变量会出现异常报错,我们需要特殊处理。
  • *.ftl后缀代码
<#--布尔类型处理 不能直接展示在页面  要转为字符串-->
<#--第一种方式: ?c转换为字符串-->
<h1>${bool?c}</h1>
<#--第二种方式:?string转换为字符串-->
<h1>${bool?string}</h1>
<#--第三种方式:?then("true展示的字符串","false展示的字符串")-->
<h1>${bool?then('这个值是对的','这个值错的')}</h1>
<hr>
<#--日期类型处理 也不能直接展示,要转为字符日期型-->
<#--只显示日期-->
<h1>${date?date}</h1>
<#--只显示时间-->
<h1>${date?time}</h1>
<#--显示日期时间-->
<h1>${date?datetime}</h1>
<#--指定字符串格式化输出-->
<h1>${date?string('yyyy年MM年dd HH时mm分ss秒')}</h1>
<hr>
<#--处理字符串 字符串可以直接展示-->
<p>${str}</p>
<#--可以用常用的函数进行操作-->
<#--截取 [0,3)只截取前三个字符-->
<p>${str?substring(0,3)}</p>
<#--首字符大写-->
<p>${str?cap_first}</p>
<#--首字符不大写-->
<p>${str?uncap_first}</p>
<#--转为大写-->
<p>${str?upper_case}</p>
<#--转为小写-->
<p>${str?lower_case}</p>
<#--查询字符的下标 如果不存在就报错-->
<p>${str?index_of('yy')}</p>
<#--以什么开头 考虑到starts_with返回布尔值 应该转为字符串展示-->
<p>${str?starts_with('ys')?string}</p>
<#--以什么结尾-->
<p>${str?ends_with('ys')?string}</p>
<#--查询字符串的长度-->
<p>${str?length}</p>
<#--替换指定字符串-->
<p>${str?replace('ys','hello')}</p>
<hr>
<#--数值类型 默认会加,进行分隔 -->
<p>${number}</p>
<#--转为货币型-->
<p>${number?string.currency}</p>
<#--转为百分比-->
<p>${number?string.percent}</p>
<#--数值转为普通字符串-->
<p>${number?c}</p>
<#--保留指定小数位的浮点型 #表示一位小数-->
<p>${number?string('0.##')}</p>
<hr>
<#--处理null和不存在的变量-->
<#--处理不存在的变量 返回布尔 不存在返回false-->
<p>${a???c}</p>
<#--处理null 如果这个值是null 设置默认显示字符(这个null值)-->
<p>${val!'这个null值'}</p>
<#--第二种处理方式 把null值默认设置为" "也就是不显示 不会报异常-->
<p>${val!}</p>
  • sequence数据类型处理
//Servlet代码
List<String> lists= Arrays.asList("北京","上海","重庆","武汉","贵州","深圳");
req.setAttribute("citys",lists);
String[] names=new String[]{"张三","李四","王五","陈六","赵七"};
req.setAttribute("names",names);
List<UserInfo> userInfos=new ArrayList<>();
userInfos.add(new UserInfo(1001,"zs","1123","bj"));
userInfos.add(new UserInfo(1002,"ls","1234","ss"));
userInfos.add(new UserInfo(1003,"ww","789456","sz"));
userInfos.add(new UserInfo(1004,"zl","12789","tj"));
req.setAttribute("userInfos",userInfos);
req.getRequestDispatcher("index2.ftl").forward(req,resp);
//ftl文件代码 freemarker
<#--sequence类型相当于java中的集合和数组-->
<#--打印全部数组-->
<#list citys as c>
${c}<br>
</#list>
<#--打印数组索引-->
<#list citys as index>
${index?index}
</#list>
<br>
<#--通过下标获取数组的值-->
<#list citys as index>
${index_index}
</#list>
<hr>
<#--常用的函数-->
<#--获取第一个值 ?frist 最后一个值?last 长度?size-->
${citys?size}
${citys?first}
${citys?last}<br>
<#--对值进行排序-->
<#list citys?sort as index>
${index}
</#list>
<br>
<#--对值排序反转-->
<br>
<#--对值排序反转-->
<#list citys?sort?reverse as index>
${index}
</#list>
<br>
<#list citys as i>
${i}
</#list>
${citys[1]}
<br>
<#list userInfos as u>
${u}<br>
</#list>
<br>
<#--根据指定属性进行排序-->
<#list userInfos?sort_by('userId') as u>
${u}<br>
</#list>
<#--获取指定的属性值 属性名区分大小写-->
<#list userInfos?sort_by('userId') as u>
${u.userName}<br>
</#list>
  • Hash数据类型处理
<#--打印所有key值-->
<#list cityMap?keys as city>
${city}
</#list>
<br>
<#--打印所有value值-->
<#list cityMap?values as city>
${city}
</#list>
<br>
<#--根据key获取value值-->
<#list cityMap?keys as city>
${cityMap[city]}
</#list>

3、自定义变量

assign 自定义变量指令
语法:
<#assign 变量名=值>
<#assign 变量名=值 变量名=值> (定义多个变量)
  • 测试代码
<#--自定义变量-->
<#assign a=10 b=10 c=20>
${a}--->${b}-->${c}
<br>
<#assign str="hello" arr=['zs','ls','ww']>
${str}-->${arr?join(",")}
<br>
<#list arr as a>
${a}
</#list>

4、条件判断分支

if, else, elseif 逻辑判断指令
格式:
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
<#else>
...
</#if>
注:
1. condition, condition2等:将被计算成布尔值的表达式。
2. elseif 和 else 指令 是可选的。
<#--条件分支判断-->
<#assign score=70>
<#if score lt 60>
成绩不及格!
<#elseif score gte 60 && score lt 70>
成绩及格!
<#elseif score gte 70 && score lt 90>
成绩良好!
<#elseif score gte 90 && score lte 100>
成绩优秀!
</#if> <#if a??>
数据存在!
<#else>
数据不存在!
</#if>

5、集合遍历

<#--
list指令
格式1:
<#list sequence as item>
</#list>
格式2:
<#list sequence as item>
<#else>
当没有选项时,执行else指令
</#list>
<#-- 当序列没有数据项时,使用默认信息 -->
<#assign arr=[]>
<#list arr as ad>
${ad}
<#else >
这个数组是空的。
</#list>

6、Macro自定义命令 (宏命令)

当有一部分模板片段被反复使用的时候,可以利用宏变量存储模板片段,也叫自定义指令。
可以使用 macro 指令来自定义一些自定义指令。
macro 自定义指令 (宏)
1. 基本使用
格式:
<#macro 指令名>
指令内容
</#macro>
使用:
<@指令名></@指令名>
2. 有参数的自定义指令
格式:
<#macro 指令名 参数名1 参数名2>
指令内容
</#macro>
使用:
<@指令名 参数名1=参数值1 参数名2=参数值2></@指令名>
注:
1. 指令可以被多次使用。
2. 自定义指令中可以包含字符串,也可包含内置指令 定义基本的自定义指令
将模板片段放入定义的macro的标签内部,需要调用的话,就用@指令去调用即可
address为宏变量,存储模板片段,也叫自定义指令
使用的感受:就像定义方法和调用方法
作用:复用
<#--测试Macro命令-->
<#macro number a b>
两数之和为${a+b}
</#macro>
<#--使用宏命令-->
<@number 12 12></@number> <#--无参输出-->
<#macro count>
<h1>测试无参输出效果</h1>
</#macro>
<@count></@count>
<br>
<#--打印九九乘法表-->
<#macro printNum>
<#list 1..9 as i>
<#list 1..i as j>
${j}*${i}=${i*j}&nbsp;
</#list>
<br/>
</#list>
</#macro>
<@printNum></@printNum>
<br/>
<#--动态参数-->
<#macro dynic num>
<#list 1..num as i>
<#list 1..i as j>
${j}*${i}=${i*j}&nbsp;
</#list>
<br>
</#list>
</#macro>
<@dynic 5></@dynic>

7、nested占位符

nested 指令执行自定义指令开始和结束标签中间的模板片段。嵌套的片段可以包含模板中任意合法的内容。
<#--测试Nested占位符 没有形式参数-->
<#macro company>
<h1><#nested>公司与公司开展交流合作!!</h1>
</#macro>
<@company>马士兵教育</@company>

8、import导入页面

引入外部页面,然后调用外部页面的方法。
//index5.ftl
<#--打印九九乘法-->
<#macro test>
<#list 1..9 as i >
<#list 1..i as j>
${i}*${j}=${i*j} &nbsp;
</#list>
<br>
</#list>
</#macro> <#--引入外部文件-->
<#import 'index5.ftl' as index5>
<@index5.test></@index5.test>

9、include引入页面

作用:在模板中包含另一个模板(可以是html、text、jsp等外部文件>
 <#--引入外部文件-->
<#include "index6.txt">
<#include "index5.ftl">

10、freeMarker页面静态化

此技术主要针对经常重复出现页面,没有必要每一次去查询,我们就需要页面静态化处理,只需要对重复的页面第一次查询用IO流的方式写入生成html静态文件,永久不再改变。实际上就是从数据库获取到数据,然后把模板ftl文件从文件流生成静态html页面。
  • 在D:\bigCode\FreeMarkerDemo\src\main\resources中编写ftl模板页面(static.ftl)
<meta charset="UTF-8" content="text/html">
<#-- 新闻标题 -->
<h1>${title}</h1>
<p>
新闻来源:${source} &nbsp; 发布时间:${pubTime?string("yyyy-MM-dd HH:mm")}
</p>
<#-- 新闻内容 -->
<p>
${content}
</p>
  • 后台生成代码
 public static void main(String[] args) throws IOException, TemplateException {
//实例化Configuration 配置模板类
Configuration configuration=new Configuration();
//设置字符编码
configuration.setDefaultEncoding("UTF-8");
//获取模板页面目录
File f1=new File("D:\\bigCode\\FreeMarkerDemo\\src\\main\\resources");
//加载模板页面目录文件对象
configuration.setDirectoryForTemplateLoading(f1);
//从模板页面目录中查找模板页面,生成模板对象
Template template = configuration.getTemplate("static.ftl");
//获取数据 可以从数据库去查询
Map<String,Object> map = new HashMap<>();
map.put("title", "特别就业季:稳就业情况如何? 哪些问题待解?");
map.put("source", "人民日报");
map.put("pubTime", new Date());
map.put("content", "中共中央政治局常务委员会近日召开会议强调," +
"要有针对性地开展援企、稳岗、扩就业工作," +
"做好高校毕业生、农民工等重点群体就业工作," +
"积极帮助个体工商户纾困。疫情期间,稳就业情况如何?还有哪些问题待解?" +
"记者采访了不同群体,记录这个特别的就业季。");
//随机生成时间戳静态文件名(html文件)
String fileName=System.currentTimeMillis()+".html";
//静态文件存放路径文件对象
File file=new File("D:\\bigCode\\FreeMarkerDemo\\src\\main\\webapp",fileName);
//获取文件字符写入流
FileWriter writer=new FileWriter(file);
//把数据用字符写入流写入模板中
template.process(map,writer);
System.out.println("文件生成成功!!"); }

FreeMark模板基本知识的更多相关文章

  1. c++11-17 模板核心知识(十一)—— 编写泛型库需要的基本技术

    Callables 函数对象 Function Objects 处理成员函数及额外的参数 std::invoke<>() 统一包装 泛型库的其他基本技术 Type Traits std:: ...

  2. c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

    概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...

  3. c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称(.template/->template/::template)

    tokenization与parsing 解析模板之类型的依赖名称 Dependent Names of Templates Example One Example Two Example Three ...

  4. c++11-17 模板核心知识(十五)—— 解析模板之依赖型类型名称与typename Dependent Names of Types

    模板名称的问题及解决 typename规则 C++20 typename 上篇文章c++11-17 模板核心知识(十四)-- 解析模板之依赖型模板名称 Dependent Names of Templ ...

  5. Freemark基本语法知识(转)

    FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成: 1,文本:直接输出的部分 2,注释:<#-- ... -->格式部分,不会输 ...

  6. Django模板-基础知识

    上一篇中带参数的URLconf虽然可以做到传参动态显示内容,但是最终现实的内容还是硬编码到Python代码中的 def hours_ahead(request,phours): try: phours ...

  7. springboot: 集成freemark模板引擎

    1.freemark简介(摘自:http://blog.csdn.net/liaomin416100569/article/details/78349072) 在互联网软件内容网站中 一般首页的访问量 ...

  8. 「快学springboot」SpringBoot整合freeMark模板引擎

    前言 虽然现在流行前后端分离开发和部署,但是有时候还是需要用到服务端渲染页面的.比如:需要考虑到SEO优化等问题的时候,FreeMark其实还是很有作用的.本人的博客本来是用React开发的,但是后来 ...

  9. ThinkPHP模板的知识(比较全的知识)

    php框架 一.真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格 ...

  10. smarty模板基础知识

    1.定义 Smarty是一个使用php写出来的模板引擎,它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与html代码混杂在一起PHP代码逻辑分离. 简单的讲,目的就是要使PH ...

随机推荐

  1. 圆角android

    资源地址 <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid an ...

  2. 触发器引起的ADG备库同步错误

    数据库alert日志报错ORA-16000,查看对应的trc文件,大致如下报错: *** 2020-10-27 14:09:03.340*** SESSION ID:(3340.75) 2020-10 ...

  3. vue项目打包,解决静态资源无法加载和路由加载无效(404)问题

    打包后的项目静态资源无法使用,导致页面空白 静态资源无法使用,那就说明项目打包后,图片和其他静态资源文件相对路径不对,此时找到config里面的index.js,在build模块下加入assetsPu ...

  4. ABC318 A-G 题解

    A 枚举 \(1\sim n\) 的每个数,判断是否有 \(i-M\equiv 0\pmod P\) 即可. 赛时代码 B 暴力覆盖即可,注意 \(x,y\) 均是左开右闭. 赛时代码 C 贪心的想, ...

  5. mysql语句操作

    1.从login表中选出name字段包含admin的前10条结果所有信息的sql语句 select  * from login where name like %admin% limit 0 ,10; ...

  6. 虹科分享|Redis Stack不想再让开发人员受苦了!

    什么是Redis Stack Redis Stack:整合Redis模块的功能 为了简化开发人员对较新的 Redis 模块及其提供的功能的体验,同时简化支持其功能的文档和客户端.以帮助开发人员从开始使 ...

  7. 安装了less后仍然报错:Error: Cannot find module 'less'

    结果是命令有点问题,正常来说是用下面的: npm i less –save-dev-g 然后可以正常启动了: --------------------------------------------- ...

  8. kubernetes集群部署redis5.0.6单机版

    1.首先,通过Config Map来对容器中redis应用的配置进行管理,如自定义配置文件.密码.日志路径等 redis-standalone-conf.yml apiVersion: v1 kind ...

  9. @ApiImplicitParam dataType属性失效

    最近在弄swagger,老是碰到注解属性失效问题.百度看了一大推,都是说什么版本问题.但是都不是我遇到的情况,下面直接上我遇到的问题及答案   可以看到,我直接用Integer,或者int,去到swa ...

  10. [C++]线段树 区间查询 单点修改

    线段树 区间查询 单点修改 算法思想 这个算法是用于数组的查询和修改 可以高效的进行查询修改 但是会增加内存的使用 本质上是一种 空间换时间 的算法 这个算法把一串数组无限二分 直到分的只剩下一个数据 ...