java代碼優化方案2(Aop處理Token令牌,頁面緩存)

前言

我們本次要解決的問題有兩個大點
一、每次用戶請求都需要判斷其是否身份驗證過
解決方案:使用Aop來爲每一個需要進行身份驗證後才能調用的方法寫一個註解
思路:每次調用方法前都先判斷這個方法是否攜帶註解,如果沒攜帶則需要驗證。

二、頁面緩存,對象緩存,頁面僞靜態化(應對秒殺)
①頁面緩存:在一般的情況下,就比如說整個頁面的框架不變,就數據發生了改變的情況下,可以使用頁面緩存來加大反應速率。

②對象緩存:比如在每次用戶的請求下都需要判斷,所以我們直接通過根據在登錄時存在redis值的用戶進行判斷就行,

③頁面僞靜態化,因爲秒殺不同於普通的頁面(比如每個博客),它是需要進行數據的一個交互


一、Aop處理Token令牌

在這裏插入圖片描述
這個處理aop其實是涉及到很多的一個知識點,
所以用兩個案例來講吧:
先把代碼放着,注意此代碼僅僅只是一部分,並不全,想要源代碼的可以私聊

@Aspect
@Component
@Slf4j
public class MiaoshaUserTokenAspect {
    @Autowired
    MiaoshaUserService userService;

    @Pointcut("execution( * com.javaxl.miaosha_05.controller.*.*(..))")
    public void miaoshaUserTokenCut() {
    }


    @Around("miaoshaUserTokenCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        //獲取方法的參數
        Object[] args = pjp.getArgs();

        Signature s = pjp.getSignature();//獲取簽名
        MethodSignature ms = (MethodSignature)s;//簽名轉方法簽名
        Method m = ms.getMethod();//跟據簽名獲取方法
        Annotation[] annotations = m.getAnnotations();//獲取註解
        for (Annotation annotation : annotations) {
//            如果在方法上添加了DisableToken註解,那麼此方法是不需要token令牌就能訪問的
            if(annotation instanceof DisableToken){
                return pjp.proceed(args);//就直接運行
            }
        }

        int count = 0;
        HttpServletRequest request = null;
        HttpServletResponse response = null;
        MiaoshaUser miaoshaUser = null;
//        主要是對參數數組的中的秒殺User做封裝處理
        int loop = 0;

        for (int i = 0; i < args.length; i++) {//遍歷調用的這個方法的參數
            if(args[i] instanceof HttpServletRequest){
                count ++;
                request = (HttpServletRequest)args[i];
            }else if(args[i] instanceof HttpServletResponse){
                count ++;
                response = (HttpServletResponse)args[i];
            }else if(args[i] instanceof MiaoshaUser){//找到其中的MiaoshaUser
                count ++;
                miaoshaUser = (MiaoshaUser)args[i];//放入到我們準備的容器
                loop = i;
            }
        }

        if(count == 3){//如果request,response,miaoshaUser,三個基本參數都有,則進行去Redis查找,前臺傳過來的token是否能在Redis中找到
            String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
            String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
            if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {//如果兩個都爲空,就代表着前端的token既沒有傳參數過來,也沒有通過請求頭傳過來
                throw new GlobalException(CodeMsg.SESSION_ERROR);
            }
            String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;//如果參數不爲空就用參數傳過來的,反之使用請求頭裏的cookieToken
            miaoshaUser = userService.getByToken(response, token);//這個方法是被封裝過的,就是從Redis獲取token相應的User對象
            args[loop] = miaoshaUser;//給我們的參數裏的User賦值

        }
        Object ob = pjp.proceed(args);// ob 爲方法的返回值
        return ob;
    }

    private String getCookieValue(HttpServletRequest request, String cookiName) {//判斷請求頭中是否有token的頭,如果有則獲取
        Cookie[]  cookies = request.getCookies();
        if(cookies == null || cookies.length <= 0){//沒有cookies
            return null;
        }
        for(Cookie cookie : cookies) {//有則獲取相應的值
            if(cookie.getName().equals(cookiName)) {
                return cookie.getValue();
            }
        }
        return null;
    }
}

1、如果不需要驗證身份信息

就比如我們的登錄:
在這裏插入圖片描述

在切面中的走向:
在這裏插入圖片描述


2、如果需要驗證身份信息

其他的業務方法:
在這裏插入圖片描述

接下來的走向:
在這裏插入圖片描述

二、頁面緩存,頁面僞靜態化(應對秒殺)

1、頁面緩存

其實按理來說大部分的博客,或者說一些相似的商品頁面都可以做到使用這種方式。

把整個頁面存放到redis中
OK,先放代碼

/**
	 * QPS:1267 load:15 mysql
	 * 5000 * 10
	 * QPS:2884, load:5
	 *
	 * 利用redis緩存整個商品列表
	 * */
    @RequestMapping(value="/to_list", produces="text/html")
    @ResponseBody
    public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {
    	model.addAttribute("user", user);
    	//取緩存
    	String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
    	if(!StringUtils.isEmpty(html)) {//如果自己渲染的html不爲空的話則直接返回
    		return html;
    	}
    	List<GoodsVo> goodsList = goodsService.listGoodsVo();
    	model.addAttribute("goodsList", goodsList);
//    	 return "goods_list";
		IWebContext ctx = new WebContext(request,response,
    			request.getServletContext(),request.getLocale(), model.asMap() );//把我們的基本商品信息放到web上下文裏
    	//手動渲染
    	html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);///跳轉到goods_list.html頁面
    	if(!StringUtils.isEmpty(html)) {
    		redisService.set(GoodsKey.getGoodsList, "", html);
    	}
    	return html;
    }

講解:
在這裏插入圖片描述

或者說,這個展示詳情的也是一個意思:

/**
	 * 單個頁面詳情頁放到redis緩存中,1min後過期
	 * @param request
	 * @param response
	 * @param model
	 * @param user
	 * @param goodsId
	 * @return
	 */
    @RequestMapping(value="/to_detail2/{goodsId}",produces="text/html")
    @ResponseBody
    public String detail2(HttpServletRequest request, HttpServletResponse response, Model model,MiaoshaUser user,
    		@PathVariable("goodsId")long goodsId) {
    	model.addAttribute("user", user);
    	
    	//取緩存
    	String html = redisService.get(GoodsKey.getGoodsDetail, ""+goodsId, String.class);
    	if(!StringUtils.isEmpty(html)) {
    		return html;
    	}
    	//手動渲染
    	GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
    	model.addAttribute("goods", goods);
    	
    	long startAt = goods.getStartDate().getTime();
    	long endAt = goods.getEndDate().getTime();
    	long now = System.currentTimeMillis();
    	
    	int miaoshaStatus = 0;
    	int remainSeconds = 0;
    	if(now < startAt ) {//秒殺還沒開始,倒計時
    		miaoshaStatus = 0;
    		remainSeconds = (int)((startAt - now )/1000);
    	}else  if(now > endAt){//秒殺已經結束
    		miaoshaStatus = 2;
    		remainSeconds = -1;
    	}else {//秒殺進行中
    		miaoshaStatus = 1;
    		remainSeconds = 0;
    	}
    	model.addAttribute("miaoshaStatus", miaoshaStatus);
    	model.addAttribute("remainSeconds", remainSeconds);
//        return "goods_detail";

		IWebContext ctx = new WebContext(request,response,
				request.getServletContext(),request.getLocale(), model.asMap() );
    	html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx);
    	if(!StringUtils.isEmpty(html)) {
    		redisService.set(GoodsKey.getGoodsDetail, ""+goodsId, html);
    	}
    	return html;
    }

2、頁面僞靜態化(應對秒殺)

在這裏插入圖片描述
goods_detail.htm代碼:

<!DOCTYPE HTML>
<html >
<head>
    <title>商品詳情</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <!-- jquery -->
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <!-- bootstrap -->
    <link rel="stylesheet" type="text/css" href="/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
    <!-- jquery-validator -->
    <script type="text/javascript" src="/jquery-validation/jquery.validate.min.js"></script>
    <script type="text/javascript" src="/jquery-validation/localization/messages_zh.min.js"></script>
    <!-- layer -->
    <script type="text/javascript" src="/layer/layer.js"></script>
    <!-- md5.js -->
    <script type="text/javascript" src="/js/md5.min.js"></script>
    <!-- common.js -->
    <script type="text/javascript" src="/js/common.js"></script>
    <style type="text/css">
        html,body{
            height:100%;
            width:100%;
        }
        body{
            background:url('/img/bg2.jpg') no-repeat;
            background-size:100% 100%;
        }
        #goodslist td{
            border-top:1px solid #39503f61;
        }
    </style>
</head>
<body>

<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"οnclick="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>
</body>
<script>


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();
	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 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();
	}
}
function refreshVerifyCode(){
	$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val()+"&timestamp="+new Date().getTime());
}
</script>
</html>

這樣可能就直接放兩個代碼,一個前端,一個後端,
1、在點擊時
在這裏插入圖片描述
2、點擊後跳轉到goods_detail.htm

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

3、調用的後臺代碼(基礎代碼,不過多講):

/**
	 * 在html頁面上做ajax異步刷新,達到頁面僞靜態的效果
	 * @param request
	 * @param response
	 * @param model
	 * @param user
	 * @param goodsId
	 * @return
	 */
    @RequestMapping(value="/detail/{goodsId}")
    @ResponseBody
    public Result<GoodsDetailVo> detail(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user,
										@PathVariable("goodsId")long goodsId) {
    	GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
    	long startAt = goods.getStartDate().getTime();
    	long endAt = goods.getEndDate().getTime();
    	long now = System.currentTimeMillis();
    	int miaoshaStatus = 0;
    	int remainSeconds = 0;
    	if(now < startAt ) {//秒殺還沒開始,倒計時
    		miaoshaStatus = 0;
    		remainSeconds = (int)((startAt - now )/1000);
    	}else  if(now > endAt){//秒殺已經結束
    		miaoshaStatus = 2;
    		remainSeconds = -1;
    	}else {//秒殺進行中
    		miaoshaStatus = 1;
    		remainSeconds = 0;
    	}
    	GoodsDetailVo vo = new GoodsDetailVo();
    	vo.setGoods(goods);
    	vo.setUser(user);
    	vo.setRemainSeconds(remainSeconds);
    	vo.setMiaoshaStatus(miaoshaStatus);
    	return Result.success(vo);
    }
    

後言

看代碼慢慢琢磨,因爲這是簡單案例,不適合直接使用,僅僅只是給一個思路而已,

並且在前後端分離的項目中也可能有不同,

項目代碼:
鏈接:https://pan.baidu.com/s/1lLuT_BdfpdYxuKSGe_TQqw
提取碼:iqeb

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章