介绍
1、什么是FrreMarker
?
中文文档:http://freemarker.foofun.cn/
FreeMarker
最初的设计,是被用来在MVC
模式的WEB
开发框架中生成HTML
页面的,它没有被绑定Servlet
或HTML
或任意Web
相关的东西,它也可以用在非Web
应用环境中。
FrreMarker
是一款模板引擎:即一种基于模板要改变的数据,并用来生成输出文本(HTML页面,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个类库,是一款程序员可以嵌入他们开发产品的组件!
模板编写为FreeMarker Template Language(FTL)
。它是简单的,专业的语言,不是想PHP
那样成熟的编程语言,那就意味着要准备数据在真实变成虚言中来展示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。
FreeMarker
是 免费的, 基于Apache许可证2.0版本发布。
模板 + 数据模型 = 输出
环境搭建
-
创建web项目,在web.xml添加Servet配置
<!-- FreeMarker 的 Servlet 配置 --> <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>
-
创建类继承
HttpServlet
变成Servlet@WebServlet("/ce") public class FtlServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request,response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("模板路径.ftl").forward(request,response); } }
-
创建模板,用于展示后台数据
index.ftl
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>${user}</title> </head> <body> </body> </html>
-
配置tomcat运行测试。
FreeMarker 模板中的数据类型,有如下几种:
- 布尔型:等价于 java 的 Boolean 类型,不同的是不能直接输出,可转换为字符串输出。
- 日期型:等价于 java 的 Date类型,不同的是不能直接输出,需要转换成字符串再输出。
- 数字型:等价于 java 的 int,float,double 等数值类型。
- 有三种显示形式:数值型(默认),货币型,百分比型
- 字符型:等价于 java 中的 java 中的数组,list,set 等集合类型。
- hash类型: 等价于 java 中的 Map 类型
一、数据类型
1、布尔类型
-
在Servlet中设置布尔类型的数据
// 布尔类型 request.setAttribute("flag",true);
-
ftl表达
<h3>布尔类型</h3> ${flag?c} ${flag?string} ${flag?string("今天你真棒!老陈","糟糕透了,伙计")} ${flag?then("yes","no")}
2、日期类型
在freemarker 中日期类型不能直接输出;如果输出 要先转成日期型 或字符串
- 年月日 ?date
- 时分秒 ?time
- 年月日时分秒 ?datetime
- 指定格式 ?string(“自定义格式”) y:年 M:月 d:日 H:时 m:分 s:秒
-
在Servlet中设置日期类型的数据
// 日期类型 request.setAttribute("creatDate",new Date());
-
在ftl表达展示
<h3>日期类型</h3> ${creatDate?date} <br> ${creatDate?time} <br> ${creatDate?datetime} <br> ${creatDate?string("yyyy/MM/dd HH:mm:ss")} <br>
3、数字类型
- 转字符串
- 普通字符串 ?c
- 货币型字符串 ?string.currency
- 百分比型字符串 ?string.percent
- 保留浮点型数值指定小数位(#表示一个小数位) ?string[“0.###”]
-
在Servlet中设置数值类型的数据
// 数据类型 request.setAttribute("age",20); request.setAttribute("salary",50000); request.setAttribute("avg",0.56);
-
在ftl表达展示
<h3>数值类型</h3> ${age} ${salary} ${avg} <br> ${age?c} ${salary?c} ${avg?c} <br> ${age?c} ${salary?string.currency} ${avg?c} <br> ${age?c} ${salary?string.currency} ${avg?string.percent} <br> ${avg?string["0.#"]} <br>
4、字符串操作
- 截取字符串(左闭右开) ?substring(start,end)
- 首字母小写输出 ?uncap_first
- 首字母大写输出 ?cap_first
- 字母转小写输出 ?lower_case
- 字母转大写输出 ?upper_case
- 获取字符串长度 ?length
- 是否以指定字符串开头(boolean类型) ?srarts_with(“xx”)?string
- 是否以指定字符串结尾(boolean类型) ?ends_with(“xx”)?string
- 获取指定字符的索引 ?index_of(“xx”)
- 替换指定字符串 ?replace(“xx”,“xx”)
-
在Servlet中设置数值类型的数据
// 字符串的操作 request.setAttribute("msg","Hello freemarker");
-
在ftl表达展示
<h3>字符串操作</h3> <#--hello freemarker--> 字符截取:${msg?substring(1,6)} <br> 首字母小写:${msg?uncap_first} <br> 首字母大写:${msg?cap_first} <br> 转成小写:${msg?lower_case} <br> 转成大写:${msg?upper_case} <br> 字符长度:${msg?length} <br> 是否以'H'开头:${msg?starts_with("H")?string} <br> 是否以's'结尾::${msg?ends_with("r")?string} <br> 获取指定字符的索引::${msg?index_of("o")} <br> 替换指定字符串::${msg?replace("ello","hhh")} <br>
5、字符串空值的处理
FreeMarker的变量必须赋值,否则会抛出异常,而对于FreeMarker来说,null值和不存在的变量是完全一样的,因为FreeMarker无法理解null值
FreeMarker提供两个运算符来避免空值
- ‘!’指定确实变量的默认值
- ${value!}:如果 value 值为空,则默认值是空字符串
- ${value!“默认值”}:如果 value 值为空,则默认值是‘字符串’默认值
- ‘??’:判断是否存在
- 如果变量存在返回 true,否则返回false
- ${(value??)?string}
-
在ftl表达展示
<h3>避免空值</h3> <h4>第一种方式~</h4> <p>设置默认值</p> <#--设置默认值为空字符串--> <#--${scr}--> <#--如果是空值的话,就指定一个默认的字符--> ${scr!"不存在的变量"} <h4>第二种方式</h4> <p>第二种方式:空值的判断,存在true,不存在 false</p> ${scr???then("老陈,今天看起来你真棒","老陈,看起来你今天糟糕透了!")} <hr>
6、sequence类型
作用 | 代码 |
---|---|
遍历集合 | <#list starts as str> |
下标 | ?index |
获取数组长度 | ?size |
获取第一个元素 | ?first |
获取最后一个元素 | ?last |
顺序输出 | <#list citysList as city> |
倒序输出 | <#list citysList?reverse as city> |
升序输出 | <#list citysList?sort as city> |
降序输出 | <#list citysList?sort?reverse as city> |
对象集合操作 | ${person.id} |
指定字段排序 | <#list personList?sort_by("id") as person> |
-
在Servlet中设置序列类型的数据
// sequence类型 String[] starts = new String[]{"周杰伦","林俊杰","陈奕迅","五月天"}; request.setAttribute("starts",starts); List<String> citys = Arrays.asList("北京","上哈","广州","深圳"); request.setAttribute("citysList",citys); List<Person> personList = new ArrayList<Person>(); personList.add(new Person(1,"小明")); personList.add(new Person(2,"小华")); personList.add(new Person(3,"校长")); request.setAttribute("personList",personList);
-
在ftl表达展示
<#--遍历集合和数组,index是下标--> <#list starts as str> ${str?index}--${str} </#list> <br> <#list citysList as city> ${city?index}--${city} </#list> <#--获取数组长度--> city数组的长度:${citysList?size} <br> <#--获取第一个元素--> 获取第一个元素:${citysList?first} <br> <#--获取最后一个元素--> 获取最后一个元素:${citysList?last} <br> <#--顺序输出--> 顺序输出: <#list citysList as city> ${city} </#list><br> <#--倒序输出--> 倒序输出: <#list citysList?reverse as city> ${city} </#list><br> <#--升序输出--> 升序输出: <#list citysList?sort as city> ${city} </#list><br> <#--降序输出--> 降序输出: <#list citysList?sort?reverse as city> ${city} </#list><br> <#--对象序列操作 通过get获取私有字段--> <span>对象序列操作:</span> <#list personList as person> 编号:${person.id} -- 姓名:${person.name}<br> </#list> <#--指定字段排序--> <span>指定字段排序:</span> <#list personList?sort_by("id") as person> 编号:${person.id} -- 姓名:${person.name}<br> </#list>
7、Hash Map类型
-
Map两种遍历的方法
-
获取键的集合,然后遍历键的集合
<#list cityMap?keys as key> ${key}--${cityMap[key]} <br> </#list>
-
直接获取值得集合,进行遍历
<#list cityMap?values as value> ${value} <br> </#list>
-
-
在Serv;et中设置hash类型的数据
// Map操作 Map<String,String> cityMap = new HashMap<String, String>(3); cityMap.put("sh","上海"); cityMap.put("bj","北京"); cityMap.put("sz","深圳"); request.setAttribute("cityMap",cityMap);
-
在ftl页面展示
<h3>hash数据类型</h3> <#--key遍历输出--> <#list cityMap?keys as key> ${key}--${cityMap[key]} <br> </#list> <#--value遍历输出--> <#list cityMap?values as value> ${value} <br> </#list>
二、常见指令
1、自定义变量指令
使用assign
指令,可以创建一个新的变量,或者替换一个已经存在的变量
- assign
<#assign str = "hello">
${str}
<#assign num=1 names=["长大","w阿萨德","电饭锅"]>
${num} -- ${names?join(",")}
2、逻辑判断指令
可以使用if,elseif和else指令来条件判断是否满足某些条件。
- if
- elseif
- else
<#assign score=75>
<br>
<#if score gt 90>
优秀
<#elseif score gt 70>
还行
<#else>
继续努力
</#if>
<#--判断数据是否存在-->
<#assign list = "">
<#if list??>
数据存在
<#else>
数据不存在
</#if>
3、自定义指令
可以使用macro指令来自定义一些自定义指令
<#-- 自定义宏-->
<#macro nm>
指令内容
</#macro>
<@nm></@nm>
带参数的宏
<#-- 带参宏-->
<#macro nm2 score>
<#if score=90>
天才
<#elseif score=70>
中不溜
<#else>
糟糕透了
</#if>
</#macro>
<@nm2 score=60></@nm2>
定义指令中包含内置指令的自定义指令
<#macro cfb num>
<#list 1..num as i>
<#list 1..i as j>
${j} * ${i} = ${j*i}
</#list>
<br>
</#list>
</#macro>
<@cfb num=9></@cfb>
4、占位指令
nested指令执行自定义指令开始和结束标签中间的模板片段,嵌套的片段可以包含模板中任意合法的阵容
<#macro nm>
<#nested>
指令内容
<#nested>
</#macro>
<@nm>站位指令</@nm>
5、improt导入指令
import指令可以引入一个库,它创建一个新的空命名空间,然后在哪个命名空间中执行路径的模板,可以使用引入空间中的指令。
-
commons.ftl (组件:可能被多个页面调用)
-
<#-- 带内置指令的宏--> <#macro cfb num> <#list 1..num as i> <#list 1..i as j> ${j} * ${i} = ${j*i} </#list> <br> </#list> </#macro> <@cfb num=9></@cfb>
-
-
order.ftl(调用组件:import)
-
<#import "commons.ftl" as commons> <@commons.cfb num=10></@commons.cfb>
-
6、include包含指令
可以使用include指令在你的模板中插入另一个FreeMarker模板文件,被包含模板的输出格式是include标签中出现的位置插入的,被包含的文件和包含它的模板共享变量,就像是被复制站填进去一样的。
-
indes.ftl
-
<#assign str="老陈你真棒!">
-
-
Order.ftl(include 可以就和粘贴到这里一样,)
-
<h3>包含模板</h3> <#include "indes.ftl"> ${str}
-
三、页面静态化
通过上述介绍可知FreeMarker是一种基于模板的,用来生成输出文本的通用工具,所以我们必须要定制符合自己业务的模板,然后生成自己的html页面,FreeMarker是通过freemarker,template,Configuration这个对象对模板进行加载的(它也处理创建和缓存与解析模板的工作)然后我们通过getTemplate方法获得你想要的模板,有一点要记住,freemarker,template,Configuration在你整个应用必须保证唯一实例。
- 什么是网页静态化技术:
- 随着用户访问量及数据量的增大,网页静态化技术方案如今越来越流行。到底什么是网页静态化技术呢?简单来说就是将页面以纯静态方式的形式展现。
- 网页静态化技术与缓存技术的比较。
- 共同点,都可以减小数据库的访问压力。
- 区别:
- 缓存技术适用于小规模的数据,以及一些不经仓变动的数据。
- 网页静态化技术适用于大规模但是变化不太频繁的数据。
- 网页静态化技术的应用场景:
- 新闻门户网站的文章类型频道一般都用到了网页静态化技术,点击新闻直接跳到静态化而页面。
- 电商网站的商品详情页也十分常见,我们在存储商品的时候会生成静态化页面,点击商品详情,会直接跳到生成的商品详情的静态化页面。
- 此外网页静态化及数可以结合inx这种高性能web服务器来提高并发访问量。
- 什么是FreeMarker?
FreeMarker
是一款用于Java
语言编写的模板引擎,用它可以通过模板和要更改的数据来生成输出文本(例如HTML网页,配置文件,源代码等)作为用来实现网页静态化及技术的一种手段,FreeMarker的使用大大超过了其他一些技术,对于系统中频繁使用数据库进行查询但是内容更新很小的应用,都可以用FreeMarker将网页静态化,这样就避免了大量的数据库访问请求,从而提高网站的性能。
定义模板
-
news.ftl
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Aaron</title> </head> <body> <h2 align="center">${title}</h2> <p align="center"> 新闻来源:${source} 发布时间:${pubTime} </p> <p style="text-indent: 2em"> ${content} </p> </body> </html>
-
编写Servlet
NewsServlet.java
// 创建一个模板对象 Configuration configuration = new Configuration(); // 设置加载上下文 configuration.setServletContextForTemplateLoading(getServletContext(),"/template"); // 设置模板的编码 configuration.setDefaultEncoding("utf-8"); // 获取对应的模板对象 Template template = configuration.getTemplate("news.ftl"); // 设置填充的数据 Map<String,Object> map = new HashMap<String, Object>(4); map.put("title","新闻中心"); map.put("source","中国"); map.put("pubTime",new Date()); map.put("content","网页静态化,这样就避免了大量的数据库访问请求,从而提高网站的性能。"); // 获得项目的目录 String realPath = request.getServletContext().getRealPath("/"); // 在根目录创建文件夹 File newsFile = new File(realPath+"/html"); if (!newsFile.exists()){ newsFile.mkdirs(); } // 获取生成的文件名(生成随机名称) String fileName = System.currentTimeMillis()+".html"; // 创建文件对象 File file = new File(newsFile,fileName); // 获取输出流 OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file),"utf-8"); // 生成HTML文件 try { template.process(map,writer); } catch (TemplateException e) { e.printStackTrace(); }finally { writer.flush(); writer.close(); }
四、整合FreeMarker
-
首先在pom.xml导入FreeMarker依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
-
配置freemarker,告诉项目模板的位置,/WEB-INF/views/ 并将视图解析器,换成freemarker的视图解析器
<!--配置freeMarker的模板路径 --> <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <!-- 配置freemarker的文件编码 --> <property name="defaultEncoding" value="UTF-8" /> <!-- 配置freemarker寻找模板的路径 --> <property name="templateLoaderPath" value="/WEB-INF/views/" /> </bean> <!--freemarker视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <!-- 是否在model自动把session中的attribute导入进去; --> <property name="exposeSessionAttributes" value="true" /> <!-- 配置逻辑视图自动添加的后缀名 --> <property name="suffix" value=".ftl" /> <!-- 配置视图的输出HTML的contentType --> <property name="contentType" value="text/html;charset=UTF-8" /> </bean>
当把freemarker和springmvc进行结合后,就可以编写页面的内容了
分析personal.ftl模板(嵌套信息)
-
导入头部组件
<#include "common/head-tpl.ftl"/>
-
在个人中心创建变量作为标识
<#assign currentNav="personal" />
-
在外部组件中有判断这个变量是否为空,
- 为空证明是登录过来的,让指定导航项选中
- 不为空证明不是登录过来的,不选选中
<#if currentMenu??> <script type="text/javascript"> $(".list-group-item li[name=${currentMenu}]").addClass("active"); </script> </#if>
-
-
导入菜单组件
<#include "common/navbar-tpl.ftl"/>
-
导入左部导航组件
<#include "common/leftmenu-tpl.ftl" />
-
判断用户是否绑定Email
<#if !userinfo.isBindPhone >
-
导入底部导航
<#include "common/footer-tpl.ftl" />
展示个人中心
个人中心类personal.ftl
编码时从自底向上写,思考逻辑时从上向底思考。
思考:从上向底
-
点击登录
- 控制器会调用业务层进行查询,把结果交给工具类
UserContext.putCurrent(logininfo);
放到Session对象中。
- 控制器会调用业务层进行查询,把结果交给工具类
-
登录成功
- 跳转到个人中心控制类
PersonalController
,通过登录用户的id(上面登录成功后把用户的信息通过工具类UserContext
放到Session中了,用户信息中就包括了ID,因为这三个表的ID一致,所有可以通过此ID查出用户信息和账户信息的所有信息)可以把用户信息Account
和账户信息Userinfo
的信息查出来,放到Model
中让个人中心展示。 - 通过登录用户的ID想要查出用户信息
Account
和账户信息Userinfo
的信息- 需要分别在各自的业务层实现通过ID查信息的功能。Account getById(Long id);
- 跳转到个人中心控制类
-
展示个人中心
return "personal"
后,会通过视图解析器,在上面已经把视图解析器换成了FreeMarker
的解析器了。- 通过解析器可以找到
personal.ftl
个人中心。
注意点:
-
个人中心页面会把Model的信息展示出来,不过账户总额的信息在用户的实体类中是没有对应的字段的,但是为什么会正常的显示出来呢?
-
因为这个字段有get方法并且返回的类型是
BigDecimal
类型- 只有get方法就能展示字段是因为:“Spring已经帮助我们对内容进行了封装操作,只需要有get相关的方法,那么就会封装一个对应的属性。”
-
BigDecimal
类型:可以加减乘除,进行精确运算,add(),可以进行累加操作。 详情网址:https://www.cnblogs.com/zhangyinhua/p/11545305.html
-