页面静态化介绍
页面静态化其实就是将原来的动态网页(例如通过ajax请求动态获取数据库中的数据并展示的网页)改为
通过静态化技术生成的静态网页,这样用户在访问网页时,服务器直接给用户响应静态html页面,没有
了动态查询数据库的过程。
那么这些静态HTML页面还需要我们自己去编写吗?其实并不需要,我们可以通过专门的页面静态化技
术帮我们生成所需的静态HTML页面,例如:Freemarker、thymeleaf等。
FreeMarker
什么是FreeMarker?
实际上用程序语言编写的程序就是模板。 FTL (代表FreeMarker模板语言)。 这是为编写模板设计的非常简单的编程语言。
模板(FTL编程)是由如下部分混合而成的:
文本:文本会照着原样来输出。
插值:这部分的输出会被计算的值来替换。插值由 ${ and } 所分隔(或者 #{ and },这种风格已经不建议再使用了;点击查看更多)。
FTL 标签:FTL标签和HTML标签很相似,但是它们却是给FreeMarker的指示, 而且不会打印在输出内容中。
注释:注释和HTML的注释也很相似,但它们是由 <#-- 和 -->来分隔的。注释会被FreeMarker直接忽略, 更不会在输出内容中显示。
我们来看一个具体的模板。其中的内容已经用颜色来标记了: 文本, 插值, FTL 标签, 注释。为了看到可见的 换行符, 这里使用了
请注意非常重要的一点: 插值 仅仅可以在 文本 中使用。 (也可以是字符串表达式;请参考 后续内容)
FTL 标签 不可以在其他 FTL 标签 和 插值中使用。比如, 这样做是 错误 的: <#if <#include ‘foo’>=‘bar’>…</#if>
Freemarker的指令(在下面的案列中解释)
简单使用Freemarker来生成静态化页面
搭建maven工程
pom.xml
需要引入freemarker的依耐
<?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.fs</groupId>
<artifactId>FreemarkerDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
</dependencies>
</project>
创建模板文件
模板文件中有四种元素:
1、文本,直接输出的部分 2、注释,即<#–…-->格式不会输出 3、插值(Interpolation):即${…}部分,
将使用数据模型中的部分替代输出 4、FTL指令:FreeMarker指令,和HTML标记类似,名字前加#予以
区分,不会输出
Freemarker的模板文件后缀可以任意,一般建议为ftl。
test.ftl
<html>
<head>
<meta charset="utf-8">
<title>Freemarker入门</title>
</head>
<body>
<#--我只是一个注释,我不会有任何输出 -->
${name}你好,${message}
<hr>
<#--assign指令用于在页面上定义一个变量 -->
<#--(1)定义简单类型 assign指令用于在页面上定义一个变量 定义变量方便测试生产的模板内容-->
<#assign linkman="付先生">
联系人:${linkman}
<#--(2)定义对象类型 -->
<#assign info={"mobile":"13812345678",'address':'重庆'} >
电话:${info.mobile} 地址:${info.address}
<hr>
<#--include指令用于模板文件的嵌套 引入模板文件 模板文件的目录会在java代码中指定
configuration.setDirectoryForTemplateLoading(new File(文件路径)); -->
<#include "head.ftl"/>
<hr>
<#--if指令用于判断-->
<#--(1)在模板文件中使用if指令进行判断 (2)在java代码中为success变量赋值-->
<#if success=true>
你已通过实名认证
<#else>
你未通过实名认证
</#if>
<hr>
<#--list指令用于遍历 (1)在模板文件中使用list指令进行遍历 (2)在java代码中为goodsList赋值-->
<#list goodsList as goods>
商品名称: ${goods.name} 价格:${goods.price}<br>
</#list>
</body>
</html>
head.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>头部</title>
</head>
<body>
<h1>---------------头部信息---------------</h1>
</body>
</html>
生成静态文件的java代码
使用步骤:
第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是 freemarker的版本
号。
第二步:设置模板文件所在的路径。
第三步:设置模板文件使用的字符集。一般就是 utf-8。
第四步:加载一个模板,创建一个模板对象。
第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。
第六步:创建一个 Writer 对象,一般创建 FileWriter 对象,指定生成的文件名。
第七步:调用模板对象的 process 方法输出文件。
第八步:关闭流。
package com.fs.testfreemarker;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestFreemarker {
public static void main(String[] args) throws Exception {
//1.创建配置类
Configuration configuration = new Configuration(Configuration.getVersion());
//2.设置模板所在的目录
configuration.setDirectoryForTemplateLoading(new File("E:\\IdeaProjects\\Demo01\\test\\FreemarkerDemo\\src\\main\\resources\\template"));
//3.设置字符集
configuration.setDefaultEncoding("utf-8");
//4.加载模板
// Template template = configuration.getTemplate("test.ftl");
Template template = configuration.getTemplate("test.ftl","utf-8");
//5.创建数据模型,我们模板中有 ${name}你好,${message} 所以我们通过map来设置数据
Map map = new HashMap();
map.put("name", "小付");
map.put("message", "欢迎使用freemarker!");
//为if指令赋值
map.put("success", true);
//为list指令赋值
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);
//6.创建Writer对象
FileOutputStream file= new FileOutputStream("E:\\IdeaProjects\\Demo01\\test\\FreemarkerDemo\\src\\main\\resources\\out\\testFreemarker.html");
//utf-8字符集输出
OutputStreamWriter out = new OutputStreamWriter(file, "utf-8");
//7.输出
template.process(map, out);
//8.关闭Writer对象
out.close();
}
}
运行后生成的html静态页面testFreemarker.html
<html>
<head>
<meta charset="utf-8">
<title>Freemarker入门</title>
</head>
<body>
小付你好,欢迎使用freemarker!
<hr>
联系人:付先生
电话:13812345678 地址:重庆
<hr>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>头部</title>
</head>
<body>
<h1>---------------头部信息---------------</h1>
</body>
</html>
<hr>
你已通过实名认证
<hr>
商品名称: 苹果 价格:5.8<br>
商品名称: 香蕉 价格:2.5<br>
商品名称: 橘子 价格:3.2<br>
</body>
</html>
浏览器打开
freemarker与Spring整合
applicationContext.xml
在application.xml中使用Bean标签将org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer交给spring的IOC管理
就可以使用FreeMarkerConfigurer得到Configuration 进行静态页面生成
//注入freemarker
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
//通过FreeMarkerConfigurer获得freemarker的配置类
Configuration configuration = freeMarkerConfigurer.getConfiguration();
<!-- 配置freemarker-->
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 指定模板文件的位子-->
<property name="templateLoaderPath" value="/WEB-INF/ftl"/>
<!-- 指定字符集-->
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<!-- 引入动态生成的静态页面地址,在java中使用@Value注入-->
<context:property-placeholder location="classpath:freemarker.properties"/>
freemarker.properties
# freemarker动态生成的静态页面输出的地址,实际生产部署是生产在tomcat的中,
outPut.Path=E:/IdeaProjects/projectOne/fs-health-parent/health_mobile/src/main/webapp/pages
java(下面的代码为伪代码.理解思想)
public class Test {
//注入freemarker
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
//从properties文件中注入freemarker生成的静态页面地址
@Value("${outPut.Path}")
private String outPutPath;
/*
由于我们现在health_mobile用户微信使用的页面的数据是异步请求获得的,用户每次使用套餐预约的时候,setmeal.html,setmeal_detail.html
中的套餐数据,检查组,检查项,都是每次查询数据库得到的,这样加载页面十分缓慢,效率太低,用户体验度极差等
解决上述方法,我们使用页面静态化技术,让我们用户每次访问的页面数据事写死的,静态的,每次用户访问,直接传递页面到用户端解析,不在请求
数据库查询,这个静态化页面中的数据如何填充?
我们查询的数据只要不进行修改,页面数据是不会变的,所以,我们后台只要添加了套餐信息,或者修改了套餐信息
就使用freemarker,先创建一个模板页面,页面中的数据使用 assign指令 include指令 if指令 list指令等指令(查看WEB-INF中的文件)
填充原来数据应该显示的位子,然后我们使用java代码动态的去指令的位子替换数据,然后生成一个静态页面,这样,用户访问的时候加载静态页面
就不每次访问去数据库查询数据,大大提高了效率
*/
//每当用户点击添加套餐的时候,执行生成静态页面方法
generateMobileStaticHtml();
/**
* 生成移动静态Html
* 下面的代码是伪代码,dao代表调用dao查询的数据
*/
public void generateMobileStaticHtml(){
//准备模板文件所需要的数据,先查询出所有的套餐信息,调用自己的查询所有方法
List<Setmeal> setMealList = dao.findAll();
//生成套餐列表静态页面mobile_setmeal.ftl,数据查询出来赋值给这个模板将这个模板变成静态页面
HashMap<String, Object> objectHashMap = new HashMap<>();
//将查询出的值放在map中
objectHashMap.put("setmealList",setMealList);
//调用生成静态页面,生成套餐列表静态页面
generateHtml("mobile_setmeal.ftl","m_setmeal.html",objectHashMap);
//生成套餐详情静态页面mobile_setmeal_detail.ftl数据查询出来赋值给这个模板,变成静态页面
//由于点击套餐详情页面是多个的,查询出的每个详情页面就生成一个动态页面,页面的名字由套餐的id结尾
//先遍历集合,得到每个套餐的id,在查询出每个套餐信息的详细数据,然后使用模板生成静态页面
for (Setmeal setmeal : setMealList) {
//每次遍历就封装一个map
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
//封装查询套餐信息的详细信息,封装到map中,让freemarker来解析出静态页面
stringObjectHashMap.put("setmeal",this.findSetMealDetails(setmeal.getId()));
//然后调用方法生成静态页面.名字结尾拼接上id
this.generateHtml("mobile_setmeal_detail.ftl","setmeal_detail_"+setmeal.getId()+".html",stringObjectHashMap);
}
}
/**
* 用于生成静态页面Dao指定的目录
* @param templateName 传递的模板名称
* @param htmlName 生成的模板名称
* @param objectHashMap 查询出的值,赋值给模板的指令符
*/
public void generateHtml(String templateName, String htmlName, Map<String, Object> objectHashMap){
//通过FreeMarkerConfigurer获得freemarker的配置类
Configuration configuration = freeMarkerConfigurer.getConfiguration();
//由于我们spring配置文件中添加freeMarkerConfigurer这个bean的时候指定了模板文件的地址,所以我们这里只需要传递模板文件的名就可以
Writer out = null;
try {
//给定模板文件,得到模板对象
Template template = configuration.getTemplate(templateName);
//指定模板对象生成的文件地址(在properties文件中指定了的)以及文件名称
File file = new File(outPutPath + "\\" + htmlName);
//创建字符缓冲流
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
//给模板文件写入数据,并传入一个字符输出流,
template.process(objectHashMap,out);
} catch (Exception e) {
e.printStackTrace();
}finally {
//判断字符缓存流是否为null
if (out != null){
try {
//不为null.说明有数据,那就刷新流,写到磁盘中
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}