FreeMarker
FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。
为什么要使用网页静态化技术?
网页静态化解决方案在实际开发中运用比较多,例如新闻网站,门户网站中的新闻频道或者是文章类的频道。对于电商网站的商品详细页来说,至少几百万个商品,每个商品又有大量的信息,这样的情况同样也适用于使用网页静态化来解决。
网页静态化技术和缓存技术的共同点都是为了减轻数据库的访问压力,但是具体的应用场景不同,缓存比较适合小规模的数据,而网页静态化比较适合大规模且相对变化不太频繁的数据。另外网页静态化还有利于SEO。
另外我们如果将网页以纯静态化的形式展现,就可以使用Nginx这样的高性能的web服务器来部署。Nginx可以承载5万的并发,而Tomcat只有几百。关于Nginx我们在后续的课程中会详细讲解。
今天我们就研究网页静态化技术----Freemarker 。
创建模板文件
模板文件中四种元素
1、文本,直接输出的部分
2、注释,即<#--...-->格式不会输出
3、插值(Interpolation):即${..}部分,将使用数据模型中的部分替代输出
4、FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出。
test.ftl
<html> <head> <meta charset="utf-8"> <title>Freemarker入门</title> </head> <body> <#--我只是一个注释,我不会有任何输出 --> ${name},你好。${message} </body> </html>
使用freemarker先导入依赖
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
package cn.itcats.freemarker; import java.io.File; import java.io.FileWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; import org.junit.Test; import freemarker.template.Configuration; import freemarker.template.Template; public class FreeMarkerTest { @Test public void testFreeMarker() throws Exception { //1、创建一个模板文件 //2、创建一个Configuration对象 //注意这个Configuration来自 freemarker.template Configuration configuration = new Configuration(Configuration.getVersion()); //3、设置模板文件保存的目录 configuration.setDirectoryForTemplateLoading(new File("D:/workspaces/ftl")); //4、模板文件的编码格式,一般就是utf-8 configuration.setDefaultEncoding("utf-8"); //5、加载一个模板文件,创建一个模板对象。 Template template = configuration.getTemplate("test.ftl"); //6、创建一个数据集。可以是pojo也可以是map。推荐使用map Map data = new HashMap<>(); data.put("test", "hello freemarker!"); //${test} Student student = new Student("001","name","广东省"); //7、创建一个Writer对象,指定输出文件的路径及文件名。 Writer out = new FileWriter(new File("D:/test.html")); //8、生成静态页面 //@Param1:数据 @Param2:输出流 数据data+模板test.ftl封装到test.html template.process(data, out); //9、关闭流 out.close(); } }
注意:如未插值(即某一个${name}为设置值会抛出异常)
FTL指令:
1、assign指令
此指令用于在页面上定义一个变量
(1)定义简单类型:
<#assign telephone="18800088888">
联系人:${linkman}
(2)定义对象类型:
<#assign info={"mobile":"13308886688",'address':'广东省广州市天河区'} >
电话:${info.mobile} 地址:${info.address}
1.1. 模板的语法
1.1.1. 访问map中的key
${key}
1.1.2. 访问pojo中的属性
Student对象。学号、姓名、年龄
${key.property}
1.1.3. 取集合中的数据
<#list studentList as student> ${student.id} 或 ${studnet.name} </#list>
1.1.4. 取循环中的下标
<#list studentList as student> ${student_index} </#list>
plus:获取集合大小
我们使用size函数来实现,代码如下:
共 ${studentList?size} 条记录
转换JSON字符串为对象
<#assign text="{'bank':'交通银行','account':'10101920201808224'}" /> <#-- 注意无法直接通过${date.bank}获得,因为加了""引号 --> <#assign data=text?eval /> 开户行:${data.bank} 账号:${data.account}
1.1.5. 判断(注意在freemarker中,==与=相同)
<#if student_index % 2 == 0> <#else> </#if>
1.1.6. 日期类型格式化
//Java代码,代码中对变量赋值: dataModel.put("data", new Date()); //ftl模板 系统当前时间:${data?date} //结果:2018-1-1 系统当前时间:${data?time} //结果:11:11:11 系统当前时间:${data?datetime} //结果:2018-1-1 11:11:11 自定义时间格式:${data?string("yyyy年MM月dd日 HH:mm:ss")} //结果:2018年1月1日 11:11:11
1.1.7. Null值的处理(freemarker里面不允许null值,否则抛出异常)
处理方式1: ${nullValue!} //在空值后面加! 等同于${nullValue!""} 给了默认值""
处理方式2: 判断nullValue值是否为null
用法为:variable??,如果该变量存在,返回true,否则返回false
<#if nullValue??> <!-- true --> nullValue值不为空 <#else> <!-- false --> nullValue值为null </#id>
1.1.8. Include标签
<#include “模板名称.ftl”>
//注意,引入其他模板时,被引入的模板一定要有数据,否则报错,优势:可复用,一般网站头尾部分相同,且利于后期维护。
1.1.9 数字转换为字符串并去除,分隔符
代码中对变量赋值:
map.put("point", 111111111);
修改模板:
分数 :${point}
//页面显示
分数 :111,111,111
我们会发现数字会以每三位一个分隔符显示,有些时候我们不需要这个分隔符,就需要将数字转换为字符串,使用内建函数c
分数:${point?c}
页面显示效果如下:
分数:111111111
1.2算数运算符
1.2.1、FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %
1.2.2、逻辑运算符
逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误
1.2.3、比较运算符
表达式中支持的比较运算符有如下几个: 1 =或者==:判断两个值是否相等. 2 !=:判断两个值是否不等. 3 >或者gt:判断左边值是否大于右边值 4 >=或者gte:判断左边值是否大于等于右边值 5 <或者lt:判断左边值是否小于右边值 6 <=或者lte:判断左边值是否小于等于右边值 注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误, 而且FreeMarker是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符 串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字 符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)> 如使用: <#if i>10> i大于10 <#/if> //此时会报错,使用gt替代 <#if i gt 10><#/if>就不会有问题了
2.1.1. 创建整合spring的配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/ftl/" /> <property name="defaultEncoding" value="UTF-8" /> </bean> </beans>
在src/main/resource中创建 properties/page.properties
内容为:pagedir=d:\\item\\
逻辑:
1、从spring容器中获得FreeMarkerConfigurer对象。
2、从FreeMarkerConfigurer对象中获得Configuration对象。
3、使用Configuration对象获得Template对象。
4、创建数据集
5、创建输出文件的Writer对象。
6、调用模板对象的process方法,生成文件。
7、关闭流。
@Service public class ItemPageServiceImpl implements ItemPageService { @Value("${pagedir}") private String pagedir; @Autowired private FreeMarkerConfig freeMarkerConfig; @Autowired private TbGoodsMapper goodsMapper; @Autowired private TbGoodsDescMapper goodsDescMapper; //生成html public boolean genItemHtml(Long goodsId){ try { Configuration configuration = freeMarkerConfig.getConfiguration(); Template template = configuration.getTemplate("item.ftl"); Map dataModel=new HashMap<>(); //1.加载商品表数据 TbGoods goods = goodsMapper.selectByPrimaryKey(goodsId); dataModel.put("goods", goods); //2.加载商品扩展表数据 TbGoodsDesc goodsDesc = goodsDescMapper.selectByPrimaryKey(goodsId); dataModel.put("goodsDesc", goodsDesc); Writer out=new FileWriter(pagedir+goodsId+".html"); template.process(dataModel, out); out.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }