上节课我们一起学习了如何在查询商品详情时添加缓存,这节课我们一起来学习下freemarker。
我们都知道,缓存可以提高查询性能,但是当用户访问量很大时,单纯依靠缓存已经不行了,为什么这么说呢?这是因为我们在访问商品详情的时候由于每款商品都不一样,因此每款商品的详情页都是动态生成的,每查看一次,页面就要重新渲染一次,渲染的过程是消耗时间的,当用户访问量很大时,这便在一定程度上降低了系统性能。那么怎么解决这个问题呢?这便引出了我们今天要学习的内容–“网页静态化”,所谓网页静态化,是指我们根据一定的模板,事先把要展示的页面生成静态页面,存放到服务器,当用户访问某商品详情页时便可以直接从服务器获取相应的静态页,不用重新渲染,这就在很大程度上提升了系统性能。
那么用什么技术来实现静态化呢?目前常用的两种技术是volocity和freemarker,我们学习其中最流行的freemarker。那么什么是freemarker?
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据,而在模板之外注意于要展示什么数据。
我们来看一张图,如下图所示,FreeMarker将模板和动态数据组合在一起,生成一个静态页面。
Template:hello ${name}!
Java objects:model.setName(''world");
Output:hello world!
下面我们便使用FreeMarker进行相应的测试,要想在工程中使用FreeMarker便要添加对FreeMarker的依赖,静态页面最好放到表现工程,因此我们在taotao-item-web工程的pom.xml文件中添加对FreeMarker的依赖,如下图所示(在taotao-parent工程统一定义了FreeMarker的版本)
下面我们创建一个模板(模板的扩展名随意,只是习惯性的用.ftl来命名)
创建一个测试类TestFreeMarker.java
需要注意的是,静态页面输出路径E:/freemarker/out要确保存在
我们运行上面的那个测试方法,运行成功后,在E:/freemarker/out目录下便可以看到hello.html静态html,我们双击打开它,可以在浏览器中看到hello freemarker信息,这就是我们要在模板中展示的内容,这说明模板与动态内容结合成功了!!
要学习freemarker就要学习它的语法,下面我们便依次来学习freemarker的常用语法。
第一个语法:Pojo的使用
首先创建一个Pojo类Student
下面我们便针对这个Pojo来创建一个新的模板student.ftl 模板代码如下:
下面对上节课的测试类代码做下修改,
运行上面的测试方法后会在E:/freemarker/out/目录下生成一个student.html页面,我们双击在浏览器中查看,发现出现乱码了。
这是由于文件的编码格式不是utf-8造成的,原来的编码是GBK,而我们的代码中设置的编码格式是UTF-8,因此需要将文件的编码格式也变成UTF-8
然后我们再重新运行上面的测试方法,然后重新刷新页面,可以看到乱码解决了。而且我们可以正常看到Pojo类中的信息了。
第二种语法:循环
在测试类中添加一个集合,在模板中循环遍历这个集合
模板添加的代码如下,循环的语法是"<#"后面接集合,"as stu"中的"stu"是当前循环到的对象
添加完代码之后,运行测试方法,成功后,我们再刷新静态网页,如下图所示。正常展示出了学生列表。
第三种语法:循环下标的获取
对于列表展示,如果我们想获取行的下标的话,用freemarker很容易实现,为了直观的查看下标,我们在模板的列表展示中添加一列"序号",值的话就直接使用当前对象及下划线和index如下图所示,便可以获取。${stu_index}
修改完后,我们重新运行测试方法,然后重新刷新静态页面,可以看到正确展示出了每行的下标。
第四种语法:if条件的使用
在列表展示当中经常用到隔行变色的效果,隔行变色就要用到if判断,如果是奇数行就用某种颜色,偶数行就用另外一种颜色。比如我现在想把奇数行背景颜色变为红色,把偶数行背景颜色变为蓝色,语法是"<#if"后面加判断条件,else在if条件之间(这点与我们平时的java习惯不一致)else书写语法是<#else>,如下图所示。
修改完后重新运行测试方法,然后重新刷新静态页面,可以看到已经展现出隔行变色的效果了。
第五种语法:日期处理
我们先来看默认情况下日期的展示效果,在测试类中添加一行代码(把日期加入map当中),在模板中把日期取出来,如下图所示。
修改完后,运行代码,会看到如下图所示的错误,告诉我们无法将日期转换为string类型,并且在"Tip"当中告诉了我们可以通过在date后面加?date或?time或?datetime来将日期类型转换为string。
既然告诉了我们应该怎么办,我们就按照上面的提示,先在模板的date后面加?date,如下所示。
日期处理类型:${date?date}
修改完后运行测试方法,然后重新刷新静态网页,可以看到如下图所示界面,在页面最下方展示出了日期,只是日期的格式是以"-"分隔的。
我们还可以把date后面换成?time和?datetime,这样输出的日期分别为"21:12:23"和"2017-5-16 21:12:50"。可以发现,展示的日期格式是比较固定的,那么如果我们想按自己想要的方式展示日期,该怎么办?这个其实刚才报错信息中已经给出提示信息了,如下图所示,明确的告诉了我们可以在date后面加?string(‘yyyy/MM/dd HH:mm:ss’)来展示我们的日期。
模板中date展示改成如下代码
日期处理类型:${date?string(‘yyyy/MM/dd HH:mm:ss’)}重新运行测试方法并重新刷新静态网页,如下图所示,发现正常按照我们的想法进行展示了。
第六种语法:null值的处理
在页面展示时有可能遇到null的处理,如果我们展示一个不存在的变量的话,就会变错,如下图所示,在最下方添加展示不存在的变量。
运行测试代码,会出现运行错误,错误消息提示展示的内容是null或者不存在,要解决这个问题,“Tip"给出了提示信息,告诉我们可以在变量后面加”!默认值"。
模板对于变量的展示做以下修改:null值的处理:${var!“默认值”}
运行测试方法并重新刷新静态页面,如下图所示。正常展示出了"默认值"。
还有一种处理null值的方法是使用if判断。
使用if判断null值:
<#if val??>
var是有值的
<#else>
var是Null
#if>
运行测试代码,然后重新刷新静态页面,可以看到输出了"var是Null"。
我们现在让var变量有值,方法是在测试方法中添加data.put(“var”,“123”);
运行测试方法然后重新刷新静态页面,可以看到var已经有值了。
第七种语法:添加页眉页脚
静态页面中最常见的是页眉和页脚的内容是固定的,这意味着我们不必要重复写页眉页脚,只需要各写一遍,然后需要的时候引入进来就可以了。我们举个例子,把上节课写的模板作为页脚添加进来。
重新运行测试方法,然后重新刷新静态页面,如下图所示,可以看到正常展示出页脚内容了。
有了这些语法,我们便可以制作出各种各样的模板了。
spring要与freemarker整合的话,需要两个包,一个是freemarker的jar包,另一个是spriing-context-support的Jar包,需要在taotao-item-web工程中确保对两个包的依赖
我们的taotao-item-web工程已经添加过对这两个包的依赖了,因此我们不必关心这步了。
下面我们需要在taotao-item-web工程的spriingmvc.xml文件中添加关于freemarker的配置,如下图所示。
下面写一个Controller类HtmlGenController.java
写完了代码,现在我们测试一下该方法是否能成功,我们先启动各个服务(zookeeper服务、image服务、solr服务、redis服务、activemq服务),然后依次启动taotao-manager、taotao-content、taotao-search、taotao-manager-web、taotao-portal-web、 taotao-search-web、taotao-item-web工程。启动完后,我们在地址栏输入http://localhost:8086/genhtml.html并回车,即可看到页面输出"OK"
我们到E盘的freemarker/out目录下查看生成的test.html,双击它在浏览器中查看,可以看到"spriing freemarker test"。
这就是freemarker与spring的简单结合
我们要做到网页静态化,那么需要有一套方案来操作。
首先是静态网页的名称,要知道,每个商品的详情页面都是不一样的,因此我们最好把商品的ID加".html"作为静态网页的名称。
接着,静态网页的生成时机是什么?其中一种方案是当用户点击商品详情的时候生成静态页面,但是这种情况是有严重问题的,当并发量高时,第一个人点击商品详情,然后去生成静态页面,有可能静态页面在生成的过程中有另外一个人也要访问这个商品详情页面,这时程序判断有没有这个商品的静态页面,发现有了,就去展示,但其实这时静态页面还没有生成完呢,这样就会造成页面不全的问题。另一种方案是当添加商品的时候就生成静态页面,这样有足够的时间来生成这个静态页面,后续有人访问该商品详情的时候,就可以直接查看静态页面了。
接着,静态网页生成的目录应该放到哪儿?其中一种方案是将生成的静态页面放到webapp目录下,这种方案不太好,原因是tomcat处理静态页面不是它的强项,处理jsp才是它的强项。另外一种方案是使用http服务器,http服务器又可以分为多种,其中一种Http服务器便是Apache服务器,另外一种http服务器可以使用nginx。
经过上面的方案分析,我们可以使用如下图所示的流程,商品服务(taotao-manager)添加商品的时候发布topic消息到消息队列,server1和server2是两套http服务器,这样做的好处是提高系统的可用性,server1或server2从消息队列中去获取消息,知道添加了商品,于是生成这个商品的静态页面并且放到http服务器上,当用户访问的时候通过nginx反向代理服务器(后面会学习nginx)访问其中一台http服务器,从中获取静态页面进行展示。
用户—>反向代理服务器nginx—>
server1(taotao-item-web html目录 http服务器nginx)
<----订阅topic item-add-topic<----activemq消息队列<—item-add-topic发送消息<----server2 taotao-manager