版权声明:文章为作者自己原创文章,转载请注明出处。 https://blog.csdn.net/qq_37128049/article/details/84950572
课程目标
1. 掌握Freemarker常用的指令与内建函数
2. 完成商品详细页的数据显示
3. 完成商品详细页的动态效果
4. 完成商品详细页读取SKU信息的业务逻辑
5. 完成商品审核调用功能
前述
1. 使用范围:
* 网页静态化解决方案在实际开发中运用比较多,比如新闻网站,门户网站中的新闻频道或者是文章类的频道;
2. 原理:
* 把数据库中的文件事先提取出来,通过文件io的写入,在服务器上生成这样一个文件,用户在访问的时候直接访问这个静态网页;用户访问的时候直接访问网页,而不用访问数据库了;
* 什么是Freemarker?
- Freemarker是一个用于java语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTPP。它不仅仅用用表现层的实现技术,而且还可以用于生成XML,JSP或Java等。
3. 优点:
1. 适用性广:
* 不单单是新闻栏目等,电商网站的商品详细页也有大量的信息,同样可以用网页静态化来解决;
2. 减轻数据库压力:
* 用户直接访问文件而不用访问数据库;
3. 利于SEO:
* 容易提高排名
4. 适合大规模且变化不频繁:
* 缓存只能小规模的数据,网页静态化能存储大规模的数据
5. 纯静态化可用Niginx:'
* Nginx可以承载5万并发,而tomcat只有几百;所以tomcat高并发还得需要集群;
4. 创建模板文件
* 四元素:
1. 文本, 直接输出的部分
2. 注释, 即<#-- ... --> 格式不会输出
3. 插值(interpolation):即${...} 部分,将使用数据模型中的部分替代输出
4. FTL指令:FreeMarker指令,和HTML标记类似,名字前加# 予以区分,不会输出;
FTL指令:
1. assign指令: -->达到在创建模板页面中的map集合put值的效果,指令是在模板中;
1. 定义简单类型:
<#assign linkman="周先生">
联系人:${linkman}
2. 定义对象类型:
<#assign info={"mobile":"13301231231",'address':'北京市昌平区王府街'}>
电话:${info.mobile} 地址:${info.address}
2. include指令: 包含其他的模板,用于模板文件的嵌套,可以复用;比如常见的头部尾部是复用的;
1. 创建模板文件:head.ftl:
<h1>黑马信息网</h1>
2. 在模板文件中使用include指令引入模板
<#include "head.ftl">
3. if指令:
1. 在模板文件body中:
<#-- if 指令-->
<#if success=true>
您已通过实名认证
<#else>
您未通过审核
</#if>
2. 在demo运行程序中:
map.put("success",false);
3. 分析:
* 先给出条件,条件可以为if,else两种也可以只if,然后再demo程序中必须给success一个布尔值,否则程序运行报错;
* 模板中的等号可以使用"=" 也可以使用"=="进行判断,意义一样;
4.list指令
1. 作用:可以将list中的值进行遍历输出
2. 实现步骤:
1. demo运行程序:
//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);
2. 模板表
<#list goodsList as goods>
${goods_index+1} 商品名称:${goods.name}商品价格:${goods.price}<br/>
</#list>
3. 分析:
1. 通过先给值,然后里面进行遍历输出;
2. ${goods_index} 可以打印出索引号;如果需要顺序排列+1即可;
内建函数:
5.1. 获取集合大小:
1. 格式:变量+?+函数名称
2. 代码演示:
1. 显示集合记录:
<#-- 内建函数-->
一共${goodsList?size} 条记录
5.2. 转换JSON字符串为对象:
1. 代码
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}
2. ?eval进行转换,如果是数组也可以进行转换;
5.3. 日期格式化:
1. 代码中对变量赋值:
map.put("today", new Date());
2.在模板文件中加入
当前日期:${today?date} <br>
当前时间:${today?time} <br>
当前日期+时间:${today?datetime} <br>
日期格式化: ${today?string("yyyy 年 MM 月 dd 日 hh:mm:ss")}
5.4. 数字转换为字符串
1. 代码中对变量赋值:
map.put("point", 102920122);
2. 修改模板:
累计积分:${point}
3. 我们会发现数字会以每三位一个分隔符显示,有些时候我们不需要这个分隔符,就需要将数字转为字符串,使用内建函数c:
累计积分:${point?c}
5.5. 空值运算符 " ?? "
1. 现象:如果你在模板中使用了变量但是在代码中没有对变量赋值,那么运行生成时会抛出异常。但是有些时候,有的变量确实是 null,怎么解决这个问题呢?
2. 判断某变量是否存在: " ?? "
1. 解析:用法为:variable??,如果该变量存在,返回 true,否则返回 false
2. 示例:
<#if aaa??>
aaa 变量存在
<#else>
aaa 变量不存在
</#if>
5.6. 缺失变量默认值: " ! "
1. 现象:我们除了可以判断是否为空值,额也可以使用!对null值做转换处理
2. 在模板加入:
${aaa!'bbb没有被赋值'}
3. 分析: 这个比上面的?判断更为简单,不需要if语句,这里!后的'' 也可以使用"";
运算符
1. 算数运算符:
FreeMarker 表达式中完全支持算术运算,FreeMarker 支持的算术运算符包括:+, - , * , / , %
2. 逻辑运算符:
逻辑运算符有如下几个: <#if aaa=true && bbb=false> </#if>
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误
3. 比较运算符:
表达式中支持的比较运算符有如下几个:
1 =或者==:判断两个值是否相等.
2 !=:判断两个值是否不等.
3 >或者 gt:判断左边值是否大于右边值 <#if (aaa=>1) >
4 >=或者 gte:判断左边值是否大于等于右边值
5 <或者 lt:判断左边值是否小于右边值
6 <=或者 lte:判断左边值是否小于等于右边值A=1;A!=”1” 字符串不能使用大于 大于等于 小于 小于等于
1. 举例:
<#--3. 运算符举例-->
<#if (100>10)>
100肯定比10大啊
</#if> <br/>
2. 分析:
* if里面的判断语句必须用括号引起来,如果不引起来的话大于小于使用gt,或者gte
* 因为如果不引起来,使用">"号作为大于,容易与前面的<if 标签进行融合,程序运行错误;
Demo小程序 – 前面代码完整版总结:
1. pom.xml:
<?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>cn.itcast.demo</groupId>
<artifactId>freemarkerDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.22</version>
</dependency>
</dependencies>
</project>
2. src/main/java/cn/itcast/demo/Test.java:
package cn.itcast.demo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException, TemplateException {
//创建一个配置对象,获取该对象的版本
Configuration configuration=new Configuration(Configuration.getVersion());
//获取模板所在目录
configuration.setDirectoryForTemplateLoading(new File("D:\\freemarkerDemo\\src\\main\\resources"));
//设置编码集
configuration.setDefaultEncoding("utf-8");
//获取模板对象
Template template=configuration.getTemplate("test.ftl");
//给值
Map map=new HashMap();
map.put("name","张三丰");
map.put("message","欢迎来到神奇的Freemarker世界!");
//if指令默认给值
map.put("success",false);
//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);
//给日期格式化赋值
map.put("today",new Date());
//数字转为字符串给值
map.put("point",135465);
//输出对象
Writer out=new FileWriter("D:\\freemarkerDemo\\src\\main\\java\\cn\\itcast\\demo\\test.html");
//输出
template.process(map,out);
//关闭
out.close();
}
}
3. src/main/java/cn/itcast/demo/test.html:
* 这个是输出的文件,运行后即可得到此文件,内容略;
4. src/main/resources/head.ftl:
<h1>黑马信息网</h1>
5. src/main/resources/test.ftl:
<html>
<head>
<title>demo</title>
<meta charset="utf-8">
</head>
<#-- freemarker注释:我是一个注释,不会输出-->
<!-- html注释 我会输出-->
<body>
<#--FTL指令-->
<#--FTL指令 1. include指令-->
<#include "head.ftl">
<#-- 插值 -->
${name},你好。${message}<br/>
<#--FTL指令2. assign指令-->
<#assign linkman="周先生">
${linkman} <br/>
<#--FTL指令 3. if 指令-->
<#if success=true>
您已通过实名认证
<#else>
您未通过审核
</#if><br/>
<#--FTL指令 4. list指令 -->
-----商品列表--------<br/>
<#list goodsList as goods>
${goods_index+1} 商品名称:${goods.name}商品价格:${goods.price}<br/>
</#list>
<#-- 内建函数-->
<#--内建函数 1.获取集合长度 -->
一共${goodsList?size} 条记录 <br/>
<#--内建函数 2.将JSON字符串转换为对象-->
<#assign text="{'bank':'招商银行','account':'546135615464'}">
<#assign data=text?eval>
开户行:${data.bank} 卡号:${data.account} <br/>
<#--内建函数 3.日期格式化-->
当前日期:${today?date} <br/>
当前时间:${today?time} <br/>
当前日期+时间: ${today?datetime} <br/>
自定义日期:${today?string("yyyy年MM月dd日 HH:mm:ss")} <br/>
<#--内建函数 4. 数字转为字符串-->
<#--显示普通数字-->
累计积分:${point} <br/>
<#--显示字符串数字-->
累计积分:${point?c}
<#--运算符-->
<#--1.空值运算符-->
<#if aaa??>
aaa存在: ${aaa}
<#else >
aaa不存在
</#if>
<#-- 2. 缺少变量默认值: !-->
${bbb!"bbb不存在"} <br/>
<#--3. 运算符举例-->
<#if (100>10)>
100肯定比10大啊
</#if> <br/>
</body>
</html>
实战总结:
1. 工作中使用网页静态化解决方案:
1. 环境搭建:
1. 导入依赖:pom.xml:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
2. 引入spring提供的支持:resources/spring/applicationContext-service.xml:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPage" value="/WEB-INF/ftl/"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
3. 最好还应该路径动态化,不要写死: resources/properties/page.properties: [程序不一定在windows系统中运行,Linux等没有D盘C盘,所以应该将地址提取出来]
pagedir=d:\\Item\\
2. 实战要点:
* 在调用数据库中的数据的时候,首先创建一个interface,再创建一个impl实现类实现它的获取方法;
* 在获取方法中 代码示例:
@Service
public class ItemPageServiceImpl implements ItemPageService {
//注入配置对象
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
//注入虚拟路径
@Value("${pageidr}")
private String pagedir;
//注入商品数据访问层
@Autowired
private TbGoodsMapper goodsMapper;
//注入商品详细页访问层
@Autowired
private TbGoodsDescMapper goodsDescMapper;
@Override
public boolean genItemHtml(Long goodsId) {
//获得配置对象
Configuration configuration=freeMarkerConfigurer.getConfiguration();
try {
//获得模板
Template template=configuration.getTemplate("item.ftl");
//设置字符集
configuration.setDefaultEncoding("UTF-8");
//设置数据模型
Map dataModel=new HashMap<>();
//注入主表数据
TbGoods goods=goodsMapper.selectByPrimaryKey(goodsId);
dataModel.put("goods",goods);
//注入商品扩展表数据
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;
}
}
}
* 我们创建模板的时候,应该将页面的头部和底部给提取出来,创建一个新的ftl文件,然后使用<#include>标签进行调用;
* 在展示数据的时候,需要从数据库中拿数据,但是数据库中的数据是JSON格式的,我们需要使用?eval 进行数据转换,转换成对象即可调用了;
* 在展示大量数据需要用到遍历集合的时候,可以使用 <#list> 方法进行遍历使用 ${..} 方法展示数据,使用<#if 变量??> 判断是否为空或 变量!'' 进行判断;
* 转换为对象后的数据调用的名称需要注意,因为是键值对,所以调用的时候需要参照数据库中的key值和value值,否则无法调用;
* 如果数据库中存入的数据不是键值对的JSON格式,则可以直接进行遍历输出;
* 查询面包屑分类列表的时候,可以先查询出该商品的id值,然后查出三级分类的各名称,然后再在页面中${...} 显示即可;
* 如果是选中的东西如何显示? 可以先在controller.js中创建一个方法,接收前端页面来的key值和value值;
* 在工作中比如商品详情页面的生成,可以放在商品审核的时候完成,当商品审核通过点击事件启动的时候,就生成静态页面;