今日目标:
掌握页面静态化技术
完成入门demo
熟练使用常用的demo
商品详情页面的展示
静态页面动态效果实现
一.freemarker入门Demo
1.首先为什么要使用freemarker技术?
FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。
FreeMarker 与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,JSP 或 Java 等。
解决高并发访问的问题,因为这样非常消耗性能,这样做肯定不对
1、商品数据量巨大,并且从三张表中获取数据。所以查询性能很差,用户体验很差
2、商品详情查看功能是频繁操作,如果每次从数据库中获取数据,给数据库造成很大的访问压力。
那我们如何解决呢?
1、将数据缓存到redis 数据量小,并且经常查询,很少发生变化的数据。
2、页面静态化。
像我们这么多的商品数据是不能存到redis数据库中的,我们只能采用一种页面静态化的技术,
我们考虑什么时候生成页面呢?
商品上架的时候。
tb_goods完成商品上架
*****注意:商品上架时,同步上架商品到索引库,并同时生成该商品对应的静态html页面。
商品下架时,同步删除索引库下架商品,并同时删除该商品对应的静态html页面。
freemarker模板的后缀是ftl
搭建一个工程实现freemarker入门demo
代码:
// 第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是Configuration configuration = new Configuration(Configuration.getVersion());// freemarker 的版本号。// 第二步:设置模板文件所在的路径。configuration.setDirectoryForTemplateLoading(new File("D:\\IdeaProjects\\projectAll\\FreemarkDemo\\src\\main\\resources"));// 第三步:设置模板文件使用的字符集。一般就是 utf-8.configuration.setDefaultEncoding("utf-8");// 第四步:加载一个模板,创建一个模板对象。Template template = configuration.getTemplate("test.ftl");// 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。Map map = new HashMap();map.put("name","张三");map.put("message","来自freemarker的世界");map.put("success",false);List goodList = new ArrayList();Map goods1 = new HashMap();goods1.put("name","苹果");goods1.put("price",5.8);Map goods2 = new HashMap();goods2.put("name","香蕉");goods2.put("price",9.9);goodList.add(goods1);goodList.add(goods2);map.put("goodList",goodList);//日期格式化map.put("today",new Date());//数字转化字符串map.put("point",123456789);map.put("aaa",null);map.put("bbb",null);// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。Writer out = new FileWriter(new File("F:\\test.html"));// 第七步:调用模板对象的 process 方法输出文件。template.process(map,out);// 第八步:关闭流out.close();
ftl的模板:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body><#--我是注釋標簽-->${name} 你好 ,我是 ${message}</body></html>
结果如图:
二.freemarker的常用指令
1.FTL指令
第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。Map map = new HashMap();map.put("name","张三");map.put("message","来自freemarker的世界");map.put("success",false);List goodList = new ArrayList();Map goods1 = new HashMap();goods1.put("name","苹果");goods1.put("price",5.8);Map goods2 = new HashMap();goods2.put("name","香蕉");goods2.put("price",9.9);goodList.add(goods1);goodList.add(goods2);map.put("goodList",goodList);//日期格式化map.put("today",new Date());//数字转化字符串map.put("point",123456789);map.put("aaa",null);map.put("bbb",null);// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
模板的配置:
第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。Map map = new HashMap();map.put("name","张三");map.put("message","来自freemarker的世界");map.put("success",false);List goodList = new ArrayList();Map goods1 = new HashMap();goods1.put("name","苹果");goods1.put("price",5.8);Map goods2 = new HashMap();goods2.put("name","香蕉");goods2.put("price",9.9);goodList.add(goods1);goodList.add(goods2);map.put("goodList",goodList);//日期格式化map.put("today",new Date());//数字转化字符串map.put("point",123456789);map.put("aaa",null);map.put("bbb",null);// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。
算符运算符:
1.逻辑运算符:
逻辑运算符有如下几个:
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误
2.比较运算符
1 =或者==:判断两个值是否相等.
2 !=:判断两个值是否不等.
3 >或者 gt:判断左边值是否大于右边值
4 >=或者 gte:判断左边值是否大于等于右边值
5 <或者 lt:判断左边值是否小于右边值
6 <=或者 lte:判断左边值是否小于等于右边值
注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且 FreeMarker 是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker 会把>解释成 FTL 标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>
注意:一般用()把需要的逻辑判断的扩展起来
三.商品详情页面的分析以及实现
1.首先创建需要生成的freemarker的工程
page_web page_service page_interface
2.分为两部分,组装数据和组装模板,首先组装数据
分析:我们分析商品详情页可知,需要三张表的数据 tb_goods tb_goodsdesc tb_item 我们通过组合实体类获得的三张表的数据
后台代码:
@Service@Transactionalpublic class PageServiceImpl implements PageService {//一次注入三个表得数据@Autowiredprivate TbGoodsMapper tbGoodsMapper;@Autowiredprivate TbGoodsDescMapper tbGoodsDescMapper;@Autowiredprivate TbItemMapper itemMapper;@Overridepublic Goods findOne(Long goodsId) {//获取goods表的数据TbGoods tbGoods = tbGoodsMapper.selectByPrimaryKey(goodsId);//获得goodsdesc表的数据TbGoodsDesc tbGoodsDesc = tbGoodsDescMapper.selectByPrimaryKey(goodsId);//获得item表的数据TbItemExample example = new TbItemExample();TbItemExample.Criteria criteria = example.createCriteria();criteria.andGoodsIdEqualTo(goodsId);List<TbItem> itemList = itemMapper.selectByExample(example);Goods goods = new Goods();goods.setTbGoods(tbGoods);goods.setTbGoodsDesc(tbGoodsDesc);goods.setItems(itemList);return goods;}
@RestController@RequestMapping("/page")public class PageController {@Referenceprivate PageService pageService;@Autowiredprivate FreeMarkerConfigurer freeMarkerConfigurer;@RequestMapping("/genHtml")public String genHtml(Long goodsId){try {// 第一步:创建一个 Configuration 对象,直接 new 一个对象。构造方法的参数就是Configuration configuration = freeMarkerConfigurer.getConfiguration();// freemarker 的版本号。// 第四步:加载一个模板,创建一个模板对象。Template template = configuration.getTemplate("item.ftl");// 第五步:创建一个模板使用的数据集,可以是 pojo 也可以是 map。一般是 Map。Goods goods = pageService.findOne(goodsId);List<TbItem> items = goods.getItemList();for (TbItem item : items) {//在这呢我们不要将组合实体类返回,因为返回还的遍历,取值,我们直接在这封装给map就可以了Map<String,Object> map = new HashMap<>();map.put("goods", goods);map.put("item",item);// 第六步:创建一个 Writer 对象,一般创建一 FileWriter 对象,指定生成的文件名。Writer out = new FileWriter("F:\\item\\"+item.getId()+".html");// 第七步:调用模板对象的 process 方法输出文件。template.process(map,out);// 第八步:关闭流out.close();}return "success.............";} catch (Exception e) {e.printStackTrace();return "fail............";}}}
模板的抽取:
抽取完一部分,记得及时引入刚刚抽取的代码
页面展示完后,我们添加需要的静态资源就能看到,
四.页面的展示
1.需求分析:
如下图所示:
我们知道goods表中有
category1Id category2Id category2Id
后台代码:
List<TbItem> itemList = itemMapper.selectByExample(example);//组装分类的信息String category1Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory1Id()).getName();String category2Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory2Id()).getName();String category3Name = itemCatMapper.selectByPrimaryKey(tbGoods.getCategory3Id()).getName();Goods goods = new Goods();//获取封装的的map集合Map<String,String> categoryMap = new HashMap<>();//将数据封装到map集合中categoryMap.put("category1Name",category1Name);categoryMap.put("category2Name",category2Name);categoryMap.put("category3Name",category3Name);goods.setCategoryMap(categoryMap);
注意我们一定要在goods的组合实体类中添加categoryMap的Map集合,最后注意打包到仓库
2.替换照片
{"color":"金色","url":"http://192.168.25.133/group1/M00/00/01/wKgZhVwbIGmABW-LAACnGz_9YE0694.jpg"}]--><#assign imageList=goods.tbGoodsDesc.itemImages?eval><!--放大镜效果--><div class="zoom"><#if (imageList?size>0)><!--默认第一个预览--><div id="preview" class="spec-preview"><span class="jqzoom"><img jqimg="${imageList[0].url}" src="${imageList[0].url}" width="400px" height="1000px"/></span></div>
3.实现规格的选项
<#--[{"attributeValue":["移动4G","联通3G"],"attributeName":"网络"},{"attributeValue":["32G","128G"],"attributeName":"机身内存"}]--><#assign specList=goods.tbGoodsDesc.specificationItems?eval><#list specList as spec><dl><dt><div class="fl title"><i>${spec.attributeName}</i></div></dt><#list spec.attributeValue as value><dd><a href="javascript:;" class="selected">${value}<span title="点击取消选择"> </span></a></dd></#list></dl></#list>
五.静态页面的动态效果实现
1.需求:
页面:<div class="controls"><input autocomplete="off" type="text" value="{{num}}" minnum="1" class="itxt" /><a href="javascript:void(0)" class="increment plus" ng-click="addNum(num+1)">+</a><a href="javascript:void(0)" class="increment mins" ng-click="addNum(num-1)">-</a></div>
pageController.js
app.controller("pageController",function($scope,$controller){//继承代码$controller("baseController",{$scope:$scope});//商品数量的加减动态数量$scope.num = 1;$scope.addNum = function(num){$scope.num = num;if($scope.num<1){$scope.num=1;}}})
注意:一定要在模板中映入资源
<#--映入资源--><script type="text/javascript" src="plugins/angularjs/angular.min.js"> </script><script type="text/javascript" src="js/base.js"> </script><script type="text/javascript" src="js/controller/baseController.js"> </script><script type="text/javascript" src="js/controller/pageController.js"> </script>
2.实现规格选项的动态切换
思路:通过获得规格列表,判断是否相等,如果都相等,获取它的id值,然后拼接成url地址,重新刷新页面通过location.href
app.controller("pageController",function($scope,$controller){//继承代码$controller("baseController",{$scope:$scope});//商品数量加减动态效果$scope.num=1;$scope.addNum=function(num){$scope.num=num;if($scope.num<1){$scope.num=1;}}//当前商品记录规格对象 var spec={"网络":"移动3G","机身内存":"64G"};//当前商品规格选项是否选择的方法$scope.isSelected=function(specName,specOption){if(spec[specName]==specOption){return true;}else{return false;}}//规格选项切换动态效果实现$scope.updateSpecAttribute=function(specName,specOption){//更新当前商品规格数据spec[specName]=specOption;//构建规格组合列表/*var specList=[{id:1369284,spec:{"网络":"移动3G","机身内存":"64G"}},{id:1369285,spec:{"网络":"移动3G","机身内存":"128G"}},{id:1369286,spec:{"网络":"移动4G","机身内存":"64G"}},{id:1369287,spec:{"网络":"移动4G","机身内存":"128G"}},];*/for(var i=0;i<specList.length;i++){if(matchObject(specList[i].spec,spec)){location.href=specList[i].id+".html";}}}//map1={"网络":"移动3G","机身内存":"64G"}//map2={"网络":"移动3G","机身内存":"64G","颜色":"red"}matchObject=function(map1,map2){for(var k in map1){if(map1[k]!=map2[k]){return false;}}//反向遍历,可以对于第二个多的在判断for(var k in map2){if(map2[k]!=map1[k]){return false;}}return true;}});
页面代码:
<script type="text/javascript">//当前商品记录规格对象var spec=${item.spec};//构建规格组合列表var specList=[<#list goods.itemList as item>{id:${item.id?c},spec:${item.spec}},</#list>];</script>
<#--class="selected"--><#list spec.attributeValue as value><dd><a href="javascript:;" ng-click="updateSpecAttribute('${spec.attributeName}','${value}')" class="{{isSelected('${spec.attributeName}','${value}')?'selected':''}}">${value}<span title="点击取消选择"> </span></a></dd></#list></dl></#list>