FreeMarker的主要学习内容
一、FreeMarker概述
FreeMarker中文官方手册(可结合学习)http://freemarker.foofun.cn/
FreeMarker学习笔记源码git地址(可供下载):https://github.com/L-Melted/FreeMarkerDemo.git
1.FreeMarker概念
FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language(FTL)。它是简单的,专用的语言,不是像PHP那样成熟的编程语言。那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。
这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。 它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑, 在没有程序员来修改或重新编译代码时,也可以修改页面的样式。
FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用程序,将视图从业务逻辑中抽离处理,业务中不再包括视图的展示,而是将视图交给FreeMarker来输出。虽然FreeMarker具有一些编程的能力,但通常由Java程序准备要显示的数据,由FreeMarker生成页面,通过模板显示准备的数据(如下图):
FreeMarker不是一个Web应用框架,而适合作为Web应用框架一个组件。
FreeMarker与容器无关,因为它并不知道HTTP或Servlet。FreeMarker同样可以应用于非Web应用程序环境。
FreeMarker更适合作为Model2框架(如Struts)的视图组件,你也可以在模板中使用JSP标记库。
2.FreeMarker特性
2.1通用目标:
能够生成各种文件:HTML、XML、RTF、Java源代码等等
易于嵌入到你的产品中:轻量级;不需要Servlet环境
插入式模板载入器:可以从任何源载入模板,如本地文件、数据库等等
你可以按你所需生成文本:保存到本地文件;作为Email发送;从web应用程序发送它返回给web浏览器
2.2强大的模板语言
所有常用的指令:include、if /else if/else 、循环结构
在模板中创建和改变变量
几乎在任何地方都可以使用复杂表达式来指定值
命名的宏,可以具有位置参数和嵌套内容
名字空间有助于建立和维护可重用的宏库,或将大工程分成模块,而不必担心名字冲突
输出转换块:在嵌套模板片段生成输出时,转化HTML转义、压缩、语法高亮等等;你可以定义自己的转换
2.3通用数据模型
FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量方式在模板中显示
你可以使用抽象(接口)方式表示对象(JavaBean、XML文档、SQL查询结果集等等),告诉模板开发者使用方法,使其不受技术细节的打扰。
2.4为Web准备
在模板语言中内建处理典型web相关任务(如HTML转义)的结构
能够集成到Model2 Web应用框架中作为JSP的替代
支持JSP标记库
为MVC模式设计:分离可视化设计和应用程序逻辑;分离页面设计员和程序员
2.5智能的国际化和本地化
字符集智能化(内部使用UNICODE)
数字格式本地化敏感
日期和时间格式化敏感
非US字符集可以用作标识(如变量名)
2.6强大的xml处理能力
<#recurse>和<#visit>指令(2.3版本)用于递归遍历XML树。在模板中清楚和直接的访问XML对象模型。开源论坛JForum就是使用了FreeMarker作为页面模板。
3.FreeMarker环境搭建
3.1新建Maven Web项目
项目整体架构如下:
3.2配置坐标依赖和部署插件
pom文件配置:(主要是使用到的重要依赖,如果需要其他的,可以自行添加)
<?xml version="1.0" encoding="UTF-8"?>
<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.xxxx</groupId>
<artifactId>freemarker01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>freemarker01 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>freemarker01</finalName>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.1.v20140609</version>
</plugin>
</plugins>
</build>
</project>
3.3修改配置文件web.xml:指定哪些文件是需要freemarker做处理的(放置在WEB-INF文件夹下面)
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<init-param>
<!--模板路径-->
<param-name>TemplatePath</param-name>
<!--默认在webapp目录下查找对应的模板文件-->
<param-value>/</param-value>
</init-param>
<init-param>
<!--模板默认的编码:UTF-8-->
<param-name>default_encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<!--处理所有以.ftl结尾的文件;ftl是freemarker默认的文件后缀-->
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
</web-app>
3.4编写Servlet类
package com.xxx.servlet;
import com.xxx.entity.User;
import javax.jws.WebService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/f01")
public class FreeMarker01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置数据(给模板设置数据)
req.setAttribute("msg","hello freemarker!");
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f01.ftl").forward(req,resp);
}
}
3.5新建模板文件ftl (在webapp下面创建template文件夹,在其下面创建.ftl文件)
<!--
html注释:在浏览器中可以看到的注释
-->
<#--
freemarker注释
在浏览器中不能看到的注释
freemarker的语法:
1.html所有的标签都适用
2.js与css的使用,与html中语法一致
-->
<#--获取数据 -->
${msg}
3.6启动项目
3.7访问项目
启动项目,在浏览器中输入Ip:port/url即可访问:http://localhost:9090/f01
二、FreeMarker 数据类型
FreeMarker模板中的数据类型由如下几种:
布尔型:等价于java的Boolean类型,不同的是不能直接输出,可转化为字符串输出
日期型:等价于java的Date类型,不同的是不能直接输出,需要转换为字符串再输出
数值型:等价于java中int,float,double等数值类型 (有三种显示形式:数值型(默认)、货币型、百分比型)
字符型:等价于java中的字符串,有很多内置函数
sequence类型:等价于java中的数组,list,set等集合类型
hash类型:等价于java中的Map类型
1、布尔类型
在Servlet中设置布尔类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f02")
public class FreeMarker02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//布尔类型
req.setAttribute("flag",true);
//请求转发跳转到指定的模板页面 template/f02.ftl
req.getRequestDispatcher("template/f02.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f02.ftl文件获取数据
<h4>FreeMarker 数据类型</h4>
<#--
FreeMarker 数据类型
布尔类型
在freeMarker页面中不能直接输出,如果要输出需要转换成字符串
方式一:?c
方式二:?string 或 ?string('为true时显示的内容','为false时显示的内容')
方式三:?then('为true时显示的内容','为false时显示的内容')
-->
<h5>布尔类型</h5><br/>
${flag?c}<br/>
${flag?string}<br/>
${flag?string('yes','no')}<br/>
${flag?string('喜欢','不喜欢')}<br/>
${flag?then('yes','no')}<br/>
${flag?then('喜欢','不喜欢')}<br/>
2、日期类型
在Servlet中设置日期类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f03")
public class FreeMarker03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//日期类型
req.setAttribute("createDate",new Date());
//请求转发跳转到指定的模板页面 template/f02.ftl
req.getRequestDispatcher("template/f03.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f03.ftl文件获取数据
<#--
数据类型:日期类型
在freemarker中日期类型不能直接输出:如果输出要先转成日期型或字符串
1.年月日 ?date
2.时分秒 ?time
3.年月日时分秒 ?datetime
4.指定格式 ?string ("自定义格式")
y: 年 M:月 d:日
H: 时 m:分 s:秒
-->
<#-- 输出日期格式 -->
${createDate?date} <br/>
<#-- 输出时间格式 -->
${createDate?time}<br/>
<#-- 输出日期时间格式 -->
${createDate?datetime} <br/>
<#-- 输出格式化日期格式 -->
${createDate?string("yyyy年MM月dd日 HH:mm:ss")}<br/>
3、数值类型
在Servlet中设置数值类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f04")
public class FreeMarker04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//数值类型
req.setAttribute("age",18);//数值型
req.setAttribute("salary",10000);//数值型
req.setAttribute("avg",0.545);//浮点型
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f04.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f04.ftl文件获取数据
<#--
数据类型:数值类型
在freemarker中数值类型可以直接输出;
1.转字符串
普通字符串 ?c
货币型字符串 ?string.currency
百分比型字符串 ?string.percent
2.保留浮点型数值指定小数位(#表示一个小数位)
?string["0.##"
-->
<#-- 直接输出数值型 -->
${age} <br/>
${salary} <br/>
${avg} <br/>
<#-- 将数值转换成字符串类型 -->
${salary?c} <br/>
<#-- 将数值转化成货币类型字符串 -->
${salary?string.currency} <br/>
<#-- 将数值转化成百分比类型的字符串 -->
${avg?string.percent} <br/>
<#-- 将浮点型的数值转换成指定小数位输出(四舍五入) -->
${avg?string["0.##"]} <br/>
4、字符串类型
在Servlet中设置字符串类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f05")
public class FreeMarker05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//字符串类型
req.setAttribute("msg","Hello");
req.setAttribute("msg2","freemarker");
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f05.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f05.ftl文件获取数据
<#--
数据类型:字符串类型
在freemarker中字符串类型可以直接输出:
1.截取字符串(左闭右开)?substring(start,end)
2.首字母小写输出 ?uncap_first
3.首字母大写输出 ?cap_first
4.字母转小写输出 ?lower_case
5.字母转大写输出 ?upper_case
6.获取字符串长度 ?length
7.是否以指定字符开头(boolean 类型) ?starts_with("xx")?string
8.是否以指定字符结尾(boolean 类型) ?ends_with("xx")?string
9.获取指定字符的索引 ?index_of("xx")
10.去除字符串前后空格 ?trim
11.替换指定字符串 ?replace("xx","xx")
-->
${msg} -- ${msg2} <br/>
${msg?string} -- ${msg2?string} <br/>
<#--1.截取字符串(左闭右开)?substring(start,end)-->
${msg?substring(0,2)}<br/>
<#--2.首字母小写输出 ?uncap_first-->
${msg?uncap_first}<br/>
<#--3.首字母大写输出 ?cap_first-->
${msg?cap_first}<br/>
<#--4.字母转小写输出 ?lower_case-->
${msg?lower_case}<br/>
<#--5.字母转大写输出 ?upper_case-->
${msg2?upper_case}<br/>
<#--6.获取字符串长度 ?length-->
${msg?length}<br/>
<#--7.是否以指定字符开头(boolean 类型) ?starts_with("xx")?string-->
${msg?starts_with("a")?string}<br/>
<#--8.是否以指定字符结尾(boolean 类型) ?ends_with("xx")?string-->
${msg?ends_with("o")?string}<br/>
<#--9.获取指定字符的索引 ?index_of("xx")-->
${msg2?index_of("m")}<br/>
<#--10.去除字符串前后空格 ?trim-->
${msg2?trim}<br/>
<#--11.替换指定字符串 ?replace("xx","xx")-->
${msg?replace("he","we")}<br/>
空值类型:
在Servlet中设置空值类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f06")
public class FreeMarker06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//空值类型
req.setAttribute("str1",null);
req.setAttribute("str2","");
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f06.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f05.ftl文件获取数据
<#--
字符串空值情况处理:
FreeMarker的变量必须赋值,否则就会抛出异常。而对于FreeMarker来说,null值和不存在的变量是完全一样的,因为FreeMarker无法理解null值。
FreeMarker提供两个运算符来避免空值:
①!:指定缺失变量的默认值
${value!}:如果value值为空,则默认值是空字符串
${value!"默认值"}:如果value值为空,则默认值是字符串“默认值”
②??:判断变量是否存在
如果变量存在,返回true,否则返回false
${(value??)?string}
-->
<#-- 如果值不存在,直接输出会报错 -->
<#-- ${bb}-->
<#-- 值为null的数据 -->
<#--${str1}<br/>-->
<#-- 值为空字符串的数据 -->
${str2}<br/>
<#-- 使用!,当值不存在时,默认显示空字符串 -->
${str!}<br/>
<#-- 使用!"xx",当值不存在时,默认显示指定字符串 -->
${str!"这是一个默认值"}<br/>
<#-- 使用??,判断字符串是否为空;返回布尔类型,如果想要输出,需要将布尔类型转换成字符串-->
${(str??)?string}<br/>
5、sequence类型
在Servlet中设置sequence类型的数据
package com.xxx.servlet;
import com.xxx.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.*;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f07")
public class FreeMarker07_Sequence extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//序列类型 (数组、List、Set)
//数组操作
String[] stars = new String[]{"周杰伦","林俊杰","陈奕迅","五月天"};
req.setAttribute("stars",stars);
//List操作
List<String> citys = Arrays.asList("上海","北京","杭州","深圳");
req.setAttribute("cityList",citys);
//JavaBean集合
List<User> userList = new ArrayList<>();
userList.add(new User(1,"zhangsan",22));
userList.add(new User(2,"lisi",18));
userList.add(new User(3,"wangwu",20));
req.setAttribute("userList",userList);
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f07.ftl").forward(req,resp);
}
}
在webapp的template文件下面创建f05.ftl文件获取数据
<#--
FreeMarker 数据类型
序列类型 (数组、List、Set)
通过list指令输出序列
<#list 序列名 as 元素名>
${元素名}
</#list>
获取序列的长度 ${序列名?size}
获取序列元素的下标 ${元素名?index}
获取第一个元素 ${序列名?first}
获取最后一个元素 ${序列名?last}
倒序输出 序列名?reverse
升序输出 序列名?sort
降序输出 序列名?sort?reverse
指定字段名排序 序列名?sort_by("字段名")
注:一般是JavaBean集合,对应的字段名需要提供get方法
-->
<#-- 数组操作 -->
<#list stars as star>
下标:${star?index}-姓名:${star}<br/>
</#list>
获取序列的长度: ${stars?size}<br/>
获取第一个元素: ${stars?first}<br/>
获取最后一个元素: ${stars?last}<br/>
<#-- List操作 -->
<#list cityList as city>
${city}-
</#list>
<br/>
<#--倒序输出 序列名?reverse-->
<#list cityList?reverse as city>
${city}-
</#list>
<br/>
<#--升序输出 序列名?sort-->
<#list cityList?sort as city>
${city}-
</#list>
<br/>
<#--降序输出 序列名?sort?reverse-->
<#list cityList?sort?reverse as city>
${city}-
</#list>
<br/>
<#list userList as user>
编号:${user.id} 姓名:${user.username} ${user.age}<br/>
</#list>
<#--指定字段名排序 序列名?sort_by("字段名")-->
<#list userList?sort_by("age") as user>
编号:${user.id} 姓名:${user.username} ${user.age}<br/>
</#list>
6、Hash类型
在Servlet中设置Map类型的数据
package com.xxx.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
/**
* @author: liman
* @Date: 2020/12/18 15:46
* @Description: FreeMarker 数据类型
*/
@WebServlet("/f08")
public class FreeMarker08_Hash extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//Map操作
Map<String, String> cityMap = new HashMap<>();
cityMap.put("sh", "上海");
cityMap.put("bj", "北京");
cityMap.put("sz", "深圳");
req.setAttribute("cityMap", cityMap);
//请求转发跳转到指定的模板页面 template/f01.ftl
req.getRequestDispatcher("template/f08.ftl").forward(req, resp);
}
}
在webapp的template文件下面创建f06.ftl文件获取数据
<#--
数据类型:hash类型
key遍历输出
<#list hash?keys as key>
${key} -- ${hash[key]}
</#list>
value遍历输出
<#list hash?values as value>
${value}
</#list>
-->
<#-- key遍历输出 -->
<#list cityMap?keys as key>
${key} -- ${cityMap[key]} <br/>
</#list>
<#--value遍历输出-->
<#list cityMap?values as value>
${value}<br/>
</#list>