应用场景:用户注册,电商秒杀接口,高并发 ....
#### 1.创建自定义注解
```
import java.lang.annotation.*;
/**
* @author Judge
* @date 2020/11/5 14:16
*/
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
/**
* 标识限制次数
*
* @return
*/
int limit() default 5;
/**
* 标识时间
*
* @return
*/
int sec() default 5;
}
```
#### 2.创建拦截器并通过反射对接口的访问进行限制
```
import com.ccl.coding.sso.annotation.AccessLimit;
import com.ccl.coding.sso.exception.AccessLimitException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* 创建拦截器类实现HandlerInterceptor 接口
*/
@Slf4j
@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handle)
throws Exception {
//判断handle是否为方法handle
if (handle instanceof HandlerMethod) {
//是的话转为HandleMethod
HandlerMethod handleMethod = (HandlerMethod) handle;
//获取方法
Method method = handleMethod.getMethod();
//判断该方法上是否有自定义注解@AccessLimit
if (!method.isAnnotationPresent(AccessLimit.class)) {
//不存在该注解则放行
return super.preHandle(request, response, handle);
}
//获取自定义注解对象
AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
if (accessLimit == null) {
//放行
return super.preHandle(request, response, handle);
}
//获取注解属性值
int limit = accessLimit.limit();
int sec = accessLimit.sec();
//可将用户的id加上 限制每一个用户只能访问几次
String key = request.getRequestURI();
log.info("key : {}", key);
//从redis中获取记录
Integer maxLimit = (Integer) redisTemplate.opsForValue().get(key);
if (maxLimit == null) {
//第一次,计数器设置为1,设置redis过期时间
redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
} else if (maxLimit < limit) {
//计数器加1
redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);
} else {
//BusinessException为自义定异常类
response.setContentType("text/html; charset=utf-8");
// throw new BusinessException("访问频繁,客官休息一会哦!");
// response.getWriter().write(JSON.toJSONString(new ResponseVo(-1, "访问频繁,客官休息一会哦!")));
response.getWriter().write(JSON.toJSONString(new ResponseVo(-1, "访问频繁,客官休息一会哦!")));
return false; }
}
return super.preHandle(request, response, handle);
}
}
```
#### 对访问的资源进行拦截
```
import com.ccl.coding.sso.interceptor.AccessLimitInterceptor;
import com.ccl.coding.sso.interceptor.AuthCheckInterceptor;
import com.ccl.coding.sso.interceptor.RequestContextInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author CHANG
* @date 2019/7/28 11:01
*/
@Configuration
public class InterceptorConfiguration implements WebMvcConfigurer {
@Autowired
private AuthCheckInterceptor authCheckInterceptor;
@Autowired
private AccessLimitInterceptor accessLimitInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestContextInterceptor()).addPathPatterns("/**");
registry.addInterceptor(authCheckInterceptor).addPathPatterns("/**");
registry.addInterceptor(accessLimitInterceptor).addPathPatterns("/**");
}
}
```
自定义异常类:
```
import lombok.Data;
/**
* 业务系统 RuntimeException
* 业务异常, 需要对外抛错。
* 1. 接管异常信息
* 2. 单据信息与后台信息交互
* 3. 业务错误进行回滚
*
* @author qingjiaqi
* @date 2019-09-10 09:58
*/
@Data
public class BusinessException extends RuntimeException {
public int code;
private String message;
/**
* 默认code
*/
public BusinessException() {
super();
this.code = CommonErrCode.BUSINESS.getCode();
}
/**
* 默认code码
*
* @param message
*/
public BusinessException(String message) {
super(message);
this.code = CommonErrCode.BUSINESS.code;
this.message = message;
}
/**
* 指定错误码
*
* @param code
* @param message
*/
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
public BusinessException(CommonErrCode errCode) {
super(errCode.getMsg());
this.code = errCode.getCode();
this.message = errCode.getMsg();
}
}
```
异常码:
```
/**
* 异常类
*
* @author qingjiaqi
* @date 2019-09-10 09:41
*/
public enum CommonErrCode {
NONE(0, "OK"),
LOGIN_INVALID(300000, "登录失效"),
REPEAT_LOGIN(300001, "您的账号在其他地方登录,如非本人操作,建议修改用户密码!"),
UN_LOGIN(300002, "未登陆"),
UN_REGISTER(300003, "未注册"),
ARGS_INVALID(400000, "请求参数有误"),
UNPACK_ERROR(400001, "报文解析失败"),
SIGN_INVALID(400010, "验签失败"),
DECRYPT_ERROR(400020, "解密失败"),
ACCESS_TOKEN_ERROR(400030, "获取 ACESS_TOKEN 失败"),
AUTH_TOKEN_INVALID(400300, "访问令牌非法"),
NO_PERMISSION(400310, "没有访问权限"),
NO_DATA_FOUND(400400, "没有数据"),
NETWORK_ERROR(400500, "网络通讯故障"),
BUSINESS(400600, "业务处理异常"),
BAD_REQUEST(410000, "请求被拒绝"),
SERVICE_NOT_EXISTS(420000, "服务不存在"),
SERVICE_UNAVAILABLE(420001, "服务不可用"),
SERVICE_ALREADY_EXISTS(420002, "服务已存在"),
INTERNAL_SERVER_ERROR(500000, "服务器内部错误"),
SERVICE_INVOKE_ERROR(500100, "服务调用出错"),
DB_ERROR(500200, "数据库错误"),
UNKNOW_ERROR(999999, "网络超时或未知异常"),
ERROR(999998, "网络超时或未知异常"),
AUTHCODE_ERROR(400700, "获取openId失败");
int code;
String message;
CommonErrCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMsg() {
return message;
}
}
```

Java - Redis之接口限流