简介:实现自定义注解@AccessLimit,本质为一个拦截器
定义一个注解类和一个AccessInterceptor类继成HandlerInterceptorAdapter抽象类,并重写preHandle方法。
第一步:创建一个注解类@Interface
1 import java.lang.annotation.Retention; 2 import java.lang.annotation.Target; 3 4 import static java.lang.annotation.ElementType.METHOD; 5 import static java.lang.annotation.RetentionPolicy.RUNTIME; 6 7 @Retention(RUNTIME) 8 @Target(METHOD) 9 public @interface AccessLimit { 10 int seconds(); 11 int maxCount(); 12 boolean needLogin() default true; 13 }
在注解类上使用的注解被称为元注解,本注解类使用了两个元注解@Retention(RUNTIME)和@Target(METHOD)。
@Retention(RUNTIME):@Retention()注解指定@AccessLimit的生命周期,RUNTIME表示该注解在程序运行结束之前被保留。
@Target(METHOD):@Target指定可以使用@AccessLimit的元素,METHOD参数表示可以在方法上使用该注解。更多参数请查看 java.lang.annotation.ElementType。
第二步:创建AccessLimit.java继承抽象类HandlerInterceptorAdapter.java,重写preHandle方法。
SpringMVC处理web请求的基本流程为,请求经过DispatcherServlet的分发后,按照一定的顺序执行一系列的Interceptor中的预处理方法,如果预处理方法返回true,则程序继续走向下一个预处理方法,或处理器方法;返回false,请求处理流程中断,此时需要通过response产生响应。
1 import com.alibaba.druid.util.StringUtils; 2 import com.alibaba.fastjson.JSON; 3 import com.app.miaosha.Pojo.MiaoshaUser; 4 import com.app.miaosha.Redis.AccessPrefix; 5 import com.app.miaosha.Redis.RedisService; 6 import com.app.miaosha.Result.CodeMsg; 7 import com.app.miaosha.Result.Result; 8 import com.app.miaosha.Service.MiaoshaUserService; 9 import org.springframework.beans.factory.annotation.Autowired; 10 import org.springframework.stereotype.Service; 11 import org.springframework.web.method.HandlerMethod; 12 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 13 14 import javax.servlet.http.Cookie; 15 import javax.servlet.http.HttpServletRequest; 16 import javax.servlet.http.HttpServletResponse; 17 import java.io.IOException; 18 import java.io.OutputStream; 19 20 @Service 21 public class AccessIntercepter extends HandlerInterceptorAdapter { //1.实现HandlerInterceptorAdapter抽象类成为拦截器 22 @Autowired 23 MiaoshaUserService miaoshaUserService; 24 @Autowired 25 RedisService redisService; 26 27 28 29 /* 30 * 2.预处理方法:在请求到达处理器方法之前被调用 31 * */ 32 @Override 33 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 34 35 36 if (handler instanceof HandlerMethod) { //3.HandlerMethod:对应一个@Controller下的@RequestMapping的方法 37 // 存放很多该处理器方法的信息 38 //handler:表示任意的前端传递来的请求,如对静态资源的请求 39 MiaoshaUser user = getMiaoshaUser(request,response); 40 UserContext.setUser(user); 41 HandlerMethod hm = (HandlerMethod) handler; 42 AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); //4.得到处理器方法前是否使用了@AccessLimit 43 if (accessLimit == null) { 44 return true; 45 } 46 int seconds = accessLimit.seconds(); 47 int maxCount = accessLimit.maxCount(); 48 boolean login = accessLimit.needLogin(); 49 String key = request.getRequestURI(); 50 //需要进行登录 51 if (login) { 52 if (user == null) { 53 render(response, CodeMsg.SESSION_ERROR); 54 return false; 55 } 56 key+="_"+user.getId(); 57 }else { 58 } 59 //实现接口限流 60 Integer count = redisService.get(AccessPrefix.setExpirSeconds(seconds),""+key,Integer.class); 61 if (count==null) { //如果还没有被访问过 62 redisService.set(AccessPrefix.setExpirSeconds(seconds),""+key,1); 63 }else if (count<maxCount) { //如果访问次数没有超过规定的最大值 64 redisService.incr(AccessPrefix.setExpirSeconds(seconds),""+key); 65 }else { //如果访问超过了规定的最大值 66 render(response,CodeMsg.ACCESS_LIMIT_REACHED); 67 return false; 68 } 69 } 70 return true; 71 } 72 73 74 /* 75 * 将CodeMsg放入到response中 76 * */ 77 private void render(HttpServletResponse response, CodeMsg sessionError) throws IOException { 78 OutputStream outputStream = response.getOutputStream(); 79 response.setContentType("application/json;charset=UTF-8"); 80 String cm = JSON.toJSONString(Result.error(sessionError)); 81 outputStream.write(cm.getBytes("UTF-8")); 82 outputStream.flush(); 83 outputStream.close(); 84 } 85 /** 86 * 通过request提供的cookie获得MiaoShaUser对象 87 */ 88 private MiaoshaUser getMiaoshaUser(HttpServletRequest request, HttpServletResponse response){ 89 String paramToken = request.getParameter(MiaoshaUserService.TOKEN_NAME); 90 String cookieToken = getCookieValue(request,MiaoshaUserService.TOKEN_NAME); 91 //使用cookie得到对象 92 if (StringUtils.isEmpty(cookieToken)&&StringUtils.isEmpty(paramToken)) { 93 return null; 94 } 95 String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken; //优先取paraToken 96 MiaoshaUser user = miaoshaUserService.getByToken(token,response); 97 return user; 98 } 99 100 /* 101 * 获得request中的cookie值 102 */ 103 private String getCookieValue(HttpServletRequest request, String tokenName) { 104 Cookie[] cookies = request.getCookies(); 105 if(cookies==null || cookies.length<=0){ 106 return null; 107 } 108 for (Cookie c:cookies 109 ) { 110 if (c.getName().equals(tokenName)) { 111 return c.getValue(); 112 } 113 } 114 return null; 115 } 116 }