###前言
通过代码片段分别介绍服务端渲染、客户端渲染、对象缓存三种方式的写法。
代码片段仅供参考,具体实现需要根据业务场景自行适配,但思想都是一样。

一、服务端渲染方式

####1、接口返回html页面的设置
```java
@Autowired
ThymeleafViewResolver thymeleafViewResolver;
@Autowired
ApplicationContext applicationContext;

@RequestMapping(value="/to_list", produces="text/html")

@ResponseBody

public String goodsList() {

// 业务逻辑

}

<br>

####2、先从缓存中取,有就返回。
```java
//取缓存
String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
if(!StringUtils.isEmpty(html)) {
return html;
}

3、缓存中没有,就手动渲染。

springboot1.5.x的写法:

List<GoodsVo> goodsList = goodsService.listGoodsVo();
model.addAttribute("goodsList", goodsList);
SpringWebContext ctx = new SpringWebContext(request,response, request.getServletContext(),request.getLocale(), model.asMap(), applicationContext );
//手动渲染
html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);

springboot2.x的写法:

WebContext ctx = new WebContext(request, response, request.getServletContext(),  request.getLocale(), model.asMap());
//手动渲染
html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);

4、最后再将渲染内容加入到redis

if(!StringUtils.isEmpty(html)) {
redisService.set(GoodsKey.getGoodsList, "", html);
}

二、客户端渲染方式(商品详情页)

####1、找到跳转到商品详情页的路径,修改为一个静态html的路径。
在商品列表页,找到跳转到详情页的动态路径,直接修改为一个静态路径,后缀为htm或shtml,总之不要是html即可,因为application.properties中一般会配置后缀为html的都访问templates文件夹下的。
注意代码中详情的链接,指向一个静态页面goods_detail.htm:
```html

秒杀商品列表
商品名称 商品图片 商品原价 秒杀价 库存数量 详情
详情

```

2、根据1的路径,在static目录下新建一个goods_detail.htm文件,原本动态页面上的模板语言都去掉,换成id=“xx”,然后使用ajax来渲染静态文件中的数据即可。

原始动态页面:

<div class="panel panel-default">
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span th:if="${user eq null}"> 您还没有登录,请登陆后再操作<br/></span>
<span>没有收货地址的提示。。。</span>
</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td colspan="3" th:text="${goods.goodsName}"></td>
</tr>
<tr>
<td>商品图片</td>
<td colspan="3"><img th:src="@{${goods.goodsImg}}" width="200" height="200" /></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td th:text="${#dates.format(goods.startDate, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td id="miaoshaTip">
<input type="hidden" id="remainSeconds" th:value="${remainSeconds}" />
<span th:if="${miaoshaStatus eq 0}">秒杀倒计时:<span id="countDown" th:text="${remainSeconds}"></span>秒</span>
<span th:if="${miaoshaStatus eq 1}">秒杀进行中</span>
<span th:if="${miaoshaStatus eq 2}">秒杀已结束</span>
</td>
<td>
<form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
<button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
<input type="hidden" name="goodsId" th:value="${goods.id}" />
</form>
</td>
</tr>
<tr>
<td>商品原价</td>
<td colspan="3" th:text="${goods.goodsPrice}"></td>
</tr>
<tr>
<td>秒杀价</td>
<td colspan="3" th:text="${goods.miaoshaPrice}"></td>
</tr>
<tr>
<td>库存数量</td>
<td colspan="3" th:text="${goods.stockCount}"></td>
</tr>
</table>
</div>

静态化之后的页面:可以看到,动态模板语言都去掉了,直接通过JS来赋值。

<div class="panel panel-default" style="height:100%;background-color:rgba(222,222,222,0.8)" >
<div class="panel-heading">秒杀商品详情</div>
<div class="panel-body">
<span id="userTip"> 您还没有登录,请登陆后再操作<br/></span>
<span>没有收货地址的提示。。。</span>
</div>
<table class="table" id="goodslist">
<tr>
<td>商品名称</td>
<td colspan="3" id="goodsName"></td>
</tr>
<tr>
<td>商品图片</td>
<td colspan="3"><img id="goodsImg" width="200" height="200" /></td>
</tr>
<tr>
<td>秒杀开始时间</td>
<td id="startTime"></td>
<td >
<input type="hidden" id="remainSeconds" />
<span id="miaoshaTip"></span>
</td>
<td>
<!--
<form id="miaoshaForm" method="post" action="/miaosha/do_miaosha">
<button class="btn btn-primary btn-block" type="submit" id="buyButton">立即秒杀</button>
<input type="hidden" name="goodsId" id="goodsId" />
</form>-->
<div class="row">
<div class="form-inline">
<img id="verifyCodeImg" width="80" height="32" style="display:none" onclick="refreshVerifyCode()"/>
<input id="verifyCode" class="form-control" style="display:none"/>
<button class="btn btn-primary" type="button" id="buyButton"onclick="getMiaoshaPath()">立即秒杀</button>
</div>
</div>
<input type="hidden" name="goodsId" id="goodsId" />
</td>
</tr>
<tr>
<td>商品原价</td>
<td colspan="3" id="goodsPrice"></td>
</tr>
<tr>
<td>秒杀价</td>
<td colspan="3" id="miaoshaPrice"></td>
</tr>
<tr>
<td>库存数量</td>
<td colspan="3" id="stockCount"></td>
</tr>
</table>
</div>

核心js代码:

<script>

$(function(){
getDetail();
}); function getDetail(){
var goodsId = g_getQueryString("goodsId");
$.ajax({
url:"/goods/detail/"+goodsId,
type:"GET",
success:function(data){
if(data.code == 0){
render(data.data);
}else{
layer.msg(data.msg);
}
},
error:function(){
layer.msg("客户端请求有误");
}
});
} function render(detail){
var miaoshaStatus = detail.miaoshaStatus;
var remainSeconds = detail.remainSeconds;
var goods = detail.goods;
var user = detail.user;
if(user){
$("#userTip").hide();
}
$("#goodsName").text(goods.goodsName);
$("#goodsImg").attr("src", goods.goodsImg);
$("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
$("#remainSeconds").val(remainSeconds);
$("#goodsId").val(goods.id);
$("#goodsPrice").text(goods.goodsPrice);
$("#miaoshaPrice").text(goods.miaoshaPrice);
$("#stockCount").text(goods.stockCount);
countDown(); // 判断秒杀开始状态
} // 判断秒杀开始状态
function countDown(){
var remainSeconds = $("#remainSeconds").val();
var timeout;
if(remainSeconds > 0){//秒杀还没开始,倒计时
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀倒计时:"+remainSeconds+"秒");
timeout = setTimeout(function(){
$("#countDown").text(remainSeconds - 1);
$("#remainSeconds").val(remainSeconds - 1);
countDown();
},1000);
}else if(remainSeconds == 0){//秒杀进行中
$("#buyButton").attr("disabled", false);
if(timeout){
clearTimeout(timeout);
}
$("#miaoshaTip").html("秒杀进行中");
$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val());
$("#verifyCodeImg").show();
$("#verifyCode").show();
}else{//秒杀已经结束
$("#buyButton").attr("disabled", true);
$("#miaoshaTip").html("秒杀已经结束");
$("#verifyCodeImg").hide();
$("#verifyCode").hide();
}
} </script>

3、记得服务端返回json格式数据,给静态页面做数据绑定。

三、客户端渲染方式(秒杀接口)

和第二点的操作基本一样,也是去除动态模板语言,改为ajax渲染。

不同的地方:

1)、多了一些springboot的配置;

2)、GET和POST的区别,这里一定要用POST,有一些场景比如删除操作,如果用了GET比如delete?id=XX这样的写法,那么搜索引擎扫描到会自动帮你删除了,所以一定要写清楚类型。

1、springboot-1.5.x的配置

spring.resources.add-mappings=true #是否启用默认资源处理
spring.resources.cache-period= 3600 #缓存时间
spring.resources.chain.cache=true #是否在资源链中启用缓存
spring.resources.chain.enabled=true #是否启用Spring资源处理链。默认情况下,禁用,除非至少启用了一个策略。
spring.resources.chain.gzipped=true #是否对缓存压缩
spring.resources.chain.html-application-cache=true #是否启用HTML5应用程序缓存清单重写
spring.resources.static-locations=classpath:/static/ #静态资源的位置

####2、springboot2.1.1的官方配置
```properties
spring.resources.add-mappings=true # 是否启用默认资源处理
spring.resources.cache.cachecontrol.cache-private= # 表示响应消息是针对单个用户的,不能由共享缓存存储。
spring.resources.cache.cachecontrol.cache-public= # 表示任何缓存都可以存储响应
spring.resources.cache.cachecontrol.max-age= # 响应被缓存的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.must-revalidate= # 表明,一旦缓存过期,在未与服务器重新验证之前,缓存不能使用响应。
spring.resources.cache.cachecontrol.no-cache= # 表示缓存的响应只有在服务器重新验证时才能重用
spring.resources.cache.cachecontrol.no-store= # 表示在任何情况下都不缓存响应
spring.resources.cache.cachecontrol.no-transform= # 指示中介(缓存和其他)它们不应该转换响应内容
spring.resources.cache.cachecontrol.proxy-revalidate= # 与“must-revalidate”指令的含义相同,只是它不适用于私有缓存。
spring.resources.cache.cachecontrol.s-max-age= # 响应被共享缓存缓存的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.stale-if-error= # 当遇到错误时,响应可能使用的最大时间,如果没有指定持续时间后缀,以秒为单位。
spring.resources.cache.cachecontrol.stale-while-revalidate= # 如果没有指定持续时间后缀,则响应在过期后可以提供的最长时间(以秒为单位)。
spring.resources.cache.period= # 资源处理程序提供的资源的缓存周期。如果没有指定持续时间后缀,将使用秒。
spring.resources.chain.cache=true # 是否在资源链中启用缓存。
spring.resources.chain.compressed=false # 是否启用已压缩资源(gzip, brotli)的解析。
spring.resources.chain.enabled= # 是否启用Spring资源处理链。默认情况下,禁用,除非至少启用了一个策略。
spring.resources.chain.html-application-cache=false # 是否启用HTML5应用缓存清单重写。
spring.resources.chain.strategy.content.enabled=false # 是否启用内容版本策略。
spring.resources.chain.strategy.content.paths=/** # 应用于内容版本策略的以逗号分隔的模式列表。
spring.resources.chain.strategy.fixed.enabled=false # 是否启用固定版本策略。
spring.resources.chain.strategy.fixed.paths=/** # 用于固定版本策略的以逗号分隔的模式列表。
spring.resources.chain.strategy.fixed.version= # 用于固定版本策略的版本字符串。
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ # 静态资源的位置。
```


####3、第二点在点击按钮时调用的JS方法getMiaoshaPath()如下
```javascript

```

四、对象缓存

最基本最常用的缓存处理逻辑:

  1. 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  2. 命中:应用程序从cache中取数据,取到后返回。
  3. 更新:先把数据存到数据库中,成功后,再让缓存失效。



    参考代码:
// 先查缓存,再查数据库。
public MiaoshaUser getById(long id) {
//取缓存
MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
if(user != null) {
return user;
}
//取数据库
user = miaoshaUserDao.getById(id);
if(user != null) {
redisService.set(MiaoshaUserKey.getById, ""+id, user);
}
return user;
} // 更新数据库后,缓存也要做同步更新。
public boolean updatePassword(String token, long id, String formPass) {
//取user
MiaoshaUser user = getById(id);
if(user == null) {
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
//更新数据库
MiaoshaUser toBeUpdate = new MiaoshaUser();
toBeUpdate.setId(id);
toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
miaoshaUserDao.update(toBeUpdate);
//处理缓存
redisService.delete(MiaoshaUserKey.getById, ""+id);
user.setPassword(toBeUpdate.getPassword());
redisService.set(MiaoshaUserKey.token, token, user);
return true;
}

总结

  • 服务端渲染:利用模板引擎技术生成静态html页面到指定目录或者是redis等缓存中间件中去,页面上的访问路径直接指向到该目录下的静态html,这种方式实际上还是属于服务端渲染的静态化方式。
  • 客户端渲染:将原本的模板语言渲染的html,改变为纯静态的htm/shtml,然后用js/ajax渲染数据,加上spring配置文件的相关配置指定静态文件存放路径,达到利用浏览器缓存页面的方式。推荐使用这种方式,这种属于前后端分离的客户端渲染方式,性能更好。
  • 对象缓存:对象级缓存主要是在service中对一些对象的缓存处理,要按照合理的步骤,先取缓存,再取数据库,缓存中有就返回缓存对象,没有就返回数据库数据,最后将数据库数据再放入缓存中。



    如果对缓存处理逻辑感兴趣,可以参考这篇博客:http://blog.csdn.net/tTU1EvLDeLFq5btqiK/article/details/78693323

java秒杀系列(2)- 页面静态化技术的更多相关文章

  1. php页面静态化技术;学习笔记

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. 页面静态化技术Freemarker技术的介绍及使用实例.

    一.FreeMarker简介 1.动态网页和静态网页差异 在进入主题之前我先介绍一下什么是动态网页,动态网页是指跟静态网页相对应的一种网页编程技术.静态网页,随着HTML代码的生成,页面的内容和显示效 ...

  3. 高性能Java Web 页面静态化技术(原创)

    package com.yancms.util; import java.io.*; import org.apache.commons.httpclient.*; import org.apache ...

  4. 高性能Java Web 页面静态化技术

    package com.yancms.util; import java.io.*; import org.apache.commons.httpclient.*; import org.apache ...

  5. Freemarker 页面静态化技术使用入门案例

    在访问 新闻.活动.商品 详情页面时, 路径可以是 xx[id].html, 服务器端根据请求 id, 动态生成 html 网页,下次访问数据时,无需再查询数据,直接将 html 静态页面返回.这样一 ...

  6. Freemarker页面静态化技术,activemq监听页面变动

    初步理解: 架构优化: 静态页面的访问速度优于从缓存获取数据的动态页面的访问速度: Freemarker: 导包 模板:hello.ftl <!DOCTYPE html> <html ...

  7. Thymeleaf页面静态化技术

    Teymeleaf的使用 案例一:springboot搭建Thymeleaf 1.导入依赖 2.新建html页面模板 3.新建前端控制层Controller 4.新建启动类 1.导入依赖 <?x ...

  8. java秒杀系列(1)- 秒杀方案总体思路

    前言 首先,要明确一点,高并发场景下系统的瓶颈出现在哪里,其实主要就是数据库,那么就要想办法为数据库做层层防护,减轻数据库的压力. 一.简单图示 我用一个比较简单直观的图来表达大概的处理思路 二.生产 ...

  9. Django框架开发web网站的网页优化—页面静态化

    网站优化-页面静态化 1)概念 提前将页面所用到的数据从数据库查询出来,然后生成一个静态页面,之后用户来访问的时候,直接返回静态页面. 举例:首页静态化:获取首页用到的数据表中的数据,生成静态首页in ...

随机推荐

  1. 别混淆了sizeof(数组名)和sizeof(指针)

    我们在挨个儿输出一个数组中的元素时,最常用的就是用一个for循环来实现,简单了事.比如类似下面的代码片段: for(i = 0; i< length; i++) { printf("数 ...

  2. mac环境下java项目无创建文件的权限

    1.问题: 先抛问题,由于刚刚换用mac环境,之前windows上开发的代码调试完毕,还未上线.之后上线部署之前,tl直连测试本地环境(mac)环境,功能无法使用,显示java.io.IOExcept ...

  3. [leetcode] 64. Minimum Path Sum (medium)

    原题 简单动态规划 重点是:grid[i][j] += min(grid[i][j - 1], grid[i - 1][j]); class Solution { public: int minPat ...

  4. [leetcode] 134. Gas Station (medium)

    原题 题意: 过一个循环的加油站,每个加油站可以加一定数量的油,走到下一个加油站需要消耗一定数量的油,判断能否走一圈. 思路: 一开始思路就是遍历一圈,最直接的思路. class Solution { ...

  5. SublimeText3 汉化

    Sublime Text3 汉化 Sublime Text 作为一款方便的代码编辑软件广受大家喜爱,所以在自己在汉化成功后记录该方法,方便大家使用. 安装 一个插件 package control 即 ...

  6. 前端响应式痛点解决之box-sizing

    前置 在 CSS 盒子模型的默认定义里,你对一个元素所设置的 width 与 height 只会应用到这个元素的内容区.如果这个元素有任何的 border 或 padding ,绘制到屏幕上时的盒子宽 ...

  7. 读完这篇文章,5G 就没有秘密了

    如果我们现在要制作一个 2019 年的热词排行榜,相信 5G 一定名列榜单前茅.作为第五代移动通信网络,5G 技术一直备受瞩目.随着 5G 商用牌照在国内的发放,各大手机厂商也是紧接着推出各款 5G ...

  8. 2019杭电多校第二场hdu6601 Keen On Everything But Triangle

    Keen On Everything But Triangle 题目传送门 解题思路 利用主席树求区间第k小,先求区间内最大的值,再求第二大,第三大--直到找到连续的三个数可以构成一个三角形.因为对于 ...

  9. Python_Day1_人人都爱列表

    列表由一系列按特定顺序排列的元素组成.你可以创建包含字母表中所有字母.数字0~9或 所有家庭成员姓名的列表;也可以将任何东西加入列表中,其中的元素之间可以没有任何关系. 鉴于列表通常包含多个元素,给列 ...

  10. Centos7 安装VNCserver&图形界面

    1.安装软件包. yum install tigervnc-server -y yum groups install GNOME yum groups install "Virtualiza ...