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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值