欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java代码优化方案(Aop处理Token令牌,页面缓存)

程序员文章站 2022-05-05 12:17:34
...

前言

我们本次要解决的问题有两个大点
一、每次用户请求都需要判断其是否身份验证过
解决方案:使用Aop来为每一个需要进行身份验证后才能调用的方法写一个注解
思路:每次调用方法前都先判断这个方法是否携带注解,如果没携带则需要验证。

二、页面缓存,对象缓存,页面伪静态化(应对秒杀)
①页面缓存:在一般的情况下,就比如说整个页面的框架不变,就数据发生了改变的情况下,可以使用页面缓存来加大反应速率。

②对象缓存:比如在每次用户的请求下都需要判断,所以我们直接通过根据在登录时存在redis值的用户进行判断就行,

③页面伪静态化,因为秒杀不同于普通的页面(比如每个博客),它是需要进行数据的一个交互


一、Aop处理Token令牌

java代码优化方案(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、如果不需要验证身份信息

就比如我们的登录:
java代码优化方案(Aop处理Token令牌,页面缓存)

在切面中的走向:
java代码优化方案(Aop处理Token令牌,页面缓存)


2、如果需要验证身份信息

其他的业务方法:
java代码优化方案(Aop处理Token令牌,页面缓存)

接下来的走向:
java代码优化方案(Aop处理Token令牌,页面缓存)

二、页面缓存,页面伪静态化(应对秒杀)

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;
    }

讲解:
java代码优化方案(Aop处理Token令牌,页面缓存)

或者说,这个展示详情的也是一个意思:

/**
	 * 单个页面详情页放到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、页面伪静态化(应对秒杀)

java代码优化方案(Aop处理Token令牌,页面缓存)
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、在点击时
java代码优化方案(Aop处理Token令牌,页面缓存)
2、点击后跳转到goods_detail.htm

java代码优化方案(Aop处理Token令牌,页面缓存)
java代码优化方案(Aop处理Token令牌,页面缓存)
java代码优化方案(Aop处理Token令牌,页面缓存)

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);
    }
    

后言

看代码慢慢琢磨,因为这是简单案例,不适合直接使用,仅仅只是给一个思路而已,

并且在前后端分离的项目中也可能有不同,

要源代码的可以私聊

相关标签: 代码优化设计