1.页面静态化介绍

2.Freemarker介绍

3.Freemarker入门案例
3.1环境搭建
创建maven工程并导入Freemarker的maven坐标
<dependency><groupId>freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.23</version>
</dependency>
3.2创建模板文件
模板文件中有四种元素:
- 文本,直接输出的部分
- 注释,即<#-- ... -->格式不会输出
- 插值(interpolation):即${..}部分,将使用数据模型中的部分替代输出
- FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以区分,不会输出
FreeMarker的模板文件后缀可以任意,一般建议为ftl。
在D盘创建ftl目录,在ftl目录中创建称为test.ftl的模板文件,内容如下:
<!-- 模板文件以 .ftl结尾 -->
<html>
<head><meta charset="utf-8"><title>Freemarker入门DEMO </title>
</head>
<body><#--我是一个注释,我不会有任何输出 -->${name},你好。${message}<#-- FTL指令,引入文件 --><#include "foot.ftl">
</body>
</html>
3.3生成文件
使用步骤:
- 第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarket的版本号。
- 第二步:设置模板文件所在的路径。
- 第三步:设置模板文件使用的字符集。一般是utf-8.
- 第四步:加载一个模板,创建一个模板对象。
- 第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。
- 第六步:创建一个Writer对象,一般创建FileWriter对象,指定生成的文件名。
- 第七步:调用模板对象的process方法输出文件。
- 第八步:关闭流。
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;public class FreeMarkerTest{public static void main(String[] args) throws Exception {//1.创建配置类,传入自身版本号Configuration configuration=new Configuration(Configuration.getVersion());//2.设置模板所在的目录 configuration.setDirectoryForTemplateLoading(new File("D:/freemarkerDemo/src/main/resources/"));//3.设置字符集configuration.setDefaultEncoding("utf-8");//4.加载模板Template template = configuration.getTemplate("test.ftl");//5.创建数据模型,准备模板文件中所需要的数据,通常是通过map构造Map map=new HashMap();map.put("name", "小明");map.put("message", "欢迎光临!");//6.创建Writer对象,准备输出流对象用于输出静态文件(格式也可以是.xml或.jsp)Writer out =new FileWriter(new File("d:\\freemarkerDemo\\src\\main\\resources\\test.html"));//7.输出template.process(map, out);//8.关闭Writer对象out.close();}
}

4.Freemarker指令
4.1 assign指令
在模板文件中定义一个变量
(1)定义简单类型
<#assign linkman="周先生">
联系人:${linkman}
(2)定义对象类型
<#assign info={"mobile":"13301231212",'address':'北京市昌平区王府街'} >
电话:${info.mobile} 地址:${info.address}
4.2 include指令
include指令用于模板文件的嵌套,在模板文件中包含另外一个文件,多用于抽取。
(1)创建模板文件head.ftl
<h1>XXX</h1>
(2)修改入门案例中的test.ftl,在模板文件中使用include指令引入上面建立的模板文件
<#include "header.ftl">
4.3 if指令
if 指令用于判断,控制输出语句
(1)在模板文件中使用if指令进行判断(需要闭合<#if></#if>)
<#-- 可以通过assign属性为变量赋值 -->
<#assign success=false>
<#if success=true>你已通过实名认证
<#else> 你未通过实名认证
</#if>
(2)在java代码中为变量success赋值
map.put("success", true);
Java代码和assign为同一个变量赋值,结果以assign为准。
在freemarker的判断中,可以使用= 也可以使用==
4.4 list指令
list 指令用于遍历
(1)在模板文件中使用list指令进行遍历
语法:#list 集合 as 存储遍历出的项
<#list goodsList as goods>商品名称:${goods.name} 价格:${goods.price}<br>
</#list>
(2)在java代码中为goodsList赋值
List goodsList=new ArrayList();Map goods1=new HashMap();
goods1.put("name", "苹果");
goods1.put("price", 5.8);Map goods2=new HashMap();
goods2.put("name", "香蕉");
goods2.put("price", 2.5);Map goods3=new HashMap();
goods3.put("name", "橘子");
goods3.put("price", 3.2);goodsList.add(goods1);
goodsList.add(goods2);
goodsList.add(goods3);
map.put("goodsList", goodsList);
如果想在循环中得到索引,使用循环变量+_index就可以得到。
5.生成移动端静态页面
前面我们已经学习了Freemarker的基本使用方法,下面我们就可以将Freemarker应用到项目中,帮我们生成移动端套餐列表静态页面和套餐详情静态页面。接下来我们需要思考几个问题:
(1)什么时候生成静态页面比较合适呢?
(2)将静态页面生成到什么位置呢?
(3)应该生成几个静态页面呢?
对于第一个问题,应该是当套餐数据发生改变时,需要生成静态页面,即我们通过后台系统修改套餐数据(包括新增、删除、编辑)时。
对于第二个问题,如果是在开发阶段可以将文件生成到项目工程中,如果上线后可以将文件生成到移动端系统运行的tomcat中。
对于第三个问题,套餐列表只需要一个页面就可以了,在这个页面中展示所有的套餐列表数据即可。套餐详情页面需要有多个,即一个套餐应该对应一个静态页面。
NGINX服务器,反向代理服务器
nginx(80) + tomcat(后端服务) 形式部署
静态文件(js,html,css):nginx可以直接配置静态文件所在的文件夹路径,nginx 直接配置访问
5.1环境搭建
在项目父工程的pom文件中导入framework的maven坐标
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>2.3.23</version>
</dependency>
5.2创建模板文件
在health_service_provider工程的WEB-INF目录中创建ftl目录,在ftl目录中创建模板文件mobile_setmeal.ftl和mobile_setmeal_detail.ftl文件,前者是用于生成套餐列表页面的模板文件,后者是生成套餐详情页面的模板文件。
(1)mobile_setmeal.ftl
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../img/asset-favico.ico"><title>预约</title><link rel="stylesheet" href="../css/page-health-order.css" />
</head>
<body data-spy="scroll" data-target="#myNavbar" data-offset="150">
<div class="app" id="app"><!-- 页面头部 --><div class="top-header"><span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span><span class="center">传智健康</span><span class="f-right"><i class="icon-more"></i></span></div><!-- 页面内容 --><div class="contentBox"><div class="list-column1"><ul class="list"><#-- 与动态页面 li class="list-item" v-for="setmeal in setmealList" 类似,但是setmealList取值在Ajax取,静态页面在java代码中取 --><#list setmealList as setmeal><li class="list-item"><a class="link-page" href="setmeal_detail_${setmeal.id}.html"><img class="img-object f-left" src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}" alt=""><div class="item-body"><h4 class="ellipsis item-title">${setmeal.name}</h4><p class="ellipsis-more item-desc">${setmeal.remark}</p><p class="item-keywords"><span><#if setmeal.sex == '0'>性别不限<#else><#if setmeal.sex == '1'>男<#else>女</#if></#if></span><span>${setmeal.age}</span></p></div></a></li></#list></ul></div></div>
</div>
<!-- 页面 css js -->
<script src="../plugins/vue/vue.js"></script>
<script src="../plugins/vue/axios-0.18.0.js"></script>
</body>
注意上面模板文件中每个套餐对应的超链接如下:
<#-- 对应动态页面 a class="link-page" :href="'setmeal_detail.html?id='+setmeal.id" -->
<a class="link-page" href="setmeal_detail_${setmeal.id}.html">
可以看到,链接的地址是动态构成的,如果套餐的id为1,则对应的超链接地址为setmeal_detail_1.html;如果套餐的id为5,则对应的超链接地址为setmeal_detail_5.html。所以我们需要为每个套餐生成一个套餐详情静态页面。
(2)mobile_setmeal_detail.ftl
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no,minimal-ui"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../img/asset-favico.ico"><title>预约详情</title><link rel="stylesheet" href="../css/page-health-orderDetail.css" /><script src="../plugins/vue/vue.js"></script><script src="../plugins/vue/axios-0.18.0.js"></script><script src="../plugins/healthmobile.js"></script>
</head>
<body data-spy="scroll" data-target="#myNavbar" data-offset="150">
<div id="app" class="app"><!-- 页面头部 --><div class="top-header"><span class="f-left"><i class="icon-back" onclick="history.go(-1)"></i></span><span class="center">传智健康</span><span class="f-right"><i class="icon-more"></i></span></div><!-- 页面内容 --><div class="contentBox"><div class="card"><div class="project-img"><img src="http://puco9aur6.bkt.clouddn.com/${setmeal.img}" width="100%" height="100%" /></div><div class="project-text"><h4 class="tit">${setmeal.name}</h4><p class="subtit">${setmeal.remark}</p><p class="keywords"><span><#if setmeal.sex == '0'>性别不限<#else><#if setmeal.sex == '1'>男<#else>女</#if></#if></span><span>${setmeal.age}</span></p></div></div><div class="table-listbox"><div class="box-title"><i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i><span>套餐详情</span></div><div class="box-table"><div class="table-title"><div class="tit-item flex2">项目名称</div><div class="tit-item flex3">项目内容</div><div class="tit-item flex3">项目解读</div></div><div class="table-content"><ul class="table-list"><#list setmeal.checkGroups as checkgroup><li class="table-item"><div class="item flex2">${checkgroup.name}</div><div class="item flex3"><#list checkgroup.checkItems as checkitem><label>${checkitem.name}</label></#list></div><div class="item flex3">${checkgroup.remark}</div></li></#list></ul></div><div class="box-button"><a @click="toOrderInfo()" class="order-btn">立即预约</a></div></div></div></div>
</div>
<script>var vue = new Vue({el:'#app',methods:{toOrderInfo(){window.location.href = "orderInfo.html?id=${setmeal.id}";}}});
</script>
</body>
5.3 配置文件
(1)在health_service_provider工程中创建属性文件freemarker.properties
与health_mobile跨项目(同一个服务器不同的tomcat),所以写绝对路径,可以dubbo远程调用,但不能访问里面的文件
out_put_path=D:/ideaProjects/health_parent/health_mobile/src/main/webapp/pages
通过上面的配置可以指定将静态HTML页面生成的目录位置,让其被访问到。
真正的生产环境下,不同于开发环境,直接生成在工程(health_parent)对应的tomcat下。
(2)在health_service_provider工程的Spring配置文件中配置
freemarker既可以单独使用也可以整合在spring中使用,对象由spring框架创建
<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><!--加载属性文件,后期在java代码中会使用到属性文件中定义的key和value-->
<context:property-placeholder location="classpath:freemarker.properties"/>
5.4 生成静态页面
修改health_service_provider工程中的SetmealServiceImpl类的add方法,加入生成静态页面的逻辑。
- 取出套餐所有的数据
- freemarkerConfig 得到模板
- 注入数据(套餐列表模板,套餐详情的模板)
- 生成文件
@Service(interfaceClass = SetmealService.class)
@Transactional
public class SetmealServiceImpl implements SetmealService {@Autowiredprivate FreeMarkerConfigurer freemarkerConfig;@Autowiredprivate SetmealDao setmealDao;@Autowiredprivate JedisPool jedisPool;@Value("${out_put_path}")//从属性文件读取要生成的html对应目录的路径private String outputpath ;//新增套餐,同时关联检查组public void add(Setmeal setmeal, Integer[] checkgroupIds) {setmealDao.add(setmeal);Integer setmealId = setmeal.getId();//获取套餐idthis.setSetmealAndCheckGroup(setmealId,checkgroupIds);//完成数据库操作后需要将图片名称保存到redisjedisPool.getResource().sadd(RedisConstant.SETMEAL_PIC_DB_RESOURCES,setmeal.getImg());//新增套餐后需要重新生成静态页面generateMobileStaticHtml();}//生成静态页面public void generateMobileStaticHtml() {//准备模板文件中所需的数据List<Setmeal> setmealList = this.findAll();//生成套餐列表静态页面generateMobileSetmealListHtml(setmealList);//生成套餐详情静态页面(多个)generateMobileSetmealDetailHtml(setmealList);}//生成套餐列表静态页面public void generateMobileSetmealListHtml(List<Setmeal> setmealList) {Map<String, Object> dataMap = new HashMap<String, Object>();dataMap.put("setmealList", setmealList);this.generateHtml("mobile_setmeal.ftl","m_setmeal.html",dataMap);}//生成套餐详情静态页面(多个)public void generateMobileSetmealDetailHtml(List<Setmeal> setmealList) {for (Setmeal setmeal : setmealList) {Map<String, Object> dataMap = new HashMap<String, Object>();dataMap.put("setmeal", this.findById(setmeal.getId()));this.generateHtml("mobile_setmeal_detail.ftl","setmeal_detail_"+setmeal.getId()+".html",dataMap);}}//通用的方法:用于生成静态页面(具体生成的静态文件,用哪个模板文件生成,模板文件中需要填充的数据)public void generateHtml(String templateName,String htmlPageName,Map<String, Object> dataMap){//获得spring注入的配置对象Configuration configuration = freeMarkerConfigurer.getConfiguration();Writer out = null;try {// 加载模版文件Template template = configuration.getTemplate(templateName);// 生成数据File docFile = new File(outputpath + "\\" + htmlPageName);out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));// 输出文件template.process(dataMap, out);} catch (Exception e) {e.printStackTrace();} finally {try {if (null != out) {out.flush();}} catch (Exception e2) {e2.printStackTrace();}}}
}
通过上面代码可以看到,我们生成的套餐列表页面名称为m_setmeal.html,为了能够在移动端访问到此页面,需要将移动端工程中的/pages/index.html页面的超链接地址进行修改:
<a href="/pages/m_setmeal.html" class="link-page"><div class="type-title"><h3>体检预约</h3><p>实时预约</p></div><div class="type-icon"><i class="icon-zhen"><span class="path1"></span><span class="path2"></span></i></div>
</a>
6.FreeMarker小结
6.1网页静态化技术
随着用户量以及数据量的增大,网页静态化技术方案如今越来越流行。
简单地说,网页静态化技术就是将网页以纯静态方式的形式展现(不需要Ajax实时发送请求)。
6.2网页静态化技术与缓存技术的比较
共同点:都可以减小数据库的访问压力。
区别:
(1)缓存技术适用于小规模的数据。以及一些经常变动的数据。
(2)网页静态化技术适用于大规模但是变化不太频繁的数据。
6.3网页静态化技术的应用场景
(1)新闻门户网站的文章类型频道一般都用到了网页静态化技术。点击新闻直接会跳转静态化的页面。
(2)电商网站的商品详情页也十分常用,在存储商品的时候会生成静态化页面,点击商品详情会直接跳到生成的商品详情的静态化页面。
(3)网页静态化技术可以结合Nignx这种高性能的web服务器来提高并发访问量。(开发时生成的静态页面在本地目录,而项目部署到tomcat运行,tomcat作为容器加载动态servlet内容,处理静态页面能力不如nignx,一般将生成的静态页面生成在nignx服务下)
6.4 什么是FreeMarker
FreeMarket是一款用Java语言编写的模板引擎,用它可以通过模板和要改变的数据来生成输出文本(例如HTML网页,配置文件,源代码等),作为用来实现网页静态化技术的一种手段。FreeMarket的使用率大大超过其他一些技术。对于系统中频繁使用数据库进行查询但是内容更新很小的应用,都可以用FreeMarket将网页静态化,这样就避免了大量的数据库访问请求,从而提高网站的性能。















