- Redis簡介
- 使用內(nèi)存存儲商佑,使用單線程,采用IO多路復(fù)用模型厢塘,性能極好
- 支持String,Hash,List,Set,Zset等多種數(shù)據(jù)類型
- 支持key定時失效
- 可采用AOF茶没,RDB方式進(jìn)行持久化
- AOP簡介
- 定義:面向切面編程,將跟業(yè)務(wù)邏輯無關(guān)的重復(fù)并且縈繞在方法周圍的代碼進(jìn)行抽取晚碾,提高了代碼的可重用性
-
常用注解:
- 注解簡介
- 使用條件:定義注解抓半,聲明注解的生命周期、作用域格嘁,注解實(shí)現(xiàn)體
- 定義在自定義注解上的元注解:
@Target:注解的作用域笛求,包含ElementType參數(shù)
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,//作用域在接口、類、枚舉探入、注解
/** Field declaration (includes enum constants) */
FIELD,//作用域在字段狡孔、枚舉的常量
/** Method declaration */
METHOD,//作用域在方法
/** Formal parameter declaration */
PARAMETER,//作用域在方法參數(shù)
/** Constructor declaration */
CONSTRUCTOR,//作用域在構(gòu)造器
/** Local variable declaration */
LOCAL_VARIABLE,//作用域在局部變量
/** Annotation type declaration */
ANNOTATION_TYPE,//作用域在注解
/** Package declaration */
PACKAGE,//作用域在包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,//作用域在類型參數(shù)
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE//作用域在使用類型的任何地方
}
@Retention:注解的作用域,包含RetentionPolicy參數(shù)
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,//存活在源文件中
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,//存活在字節(jié)碼文件中
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME//存活在代碼運(yùn)行期間
@Inherited:允許子類繼承父類的注解
@Documented:此注解會包含在javadoc中
-
代碼實(shí)現(xiàn)
定義注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RestrictAccess {
/**
* 限制時間蜂嗽,單位是毫秒
*/
long ttl() default 0;
/**
* 限制時間內(nèi)的訪問次數(shù)
*/
int accessFrequency();
}
RedisTemplate對象的配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Integer> redisTemplate(RedisConnectionFactory redisConnectionFactory){
final RedisTemplate<String,Integer> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericToStringSerializer<Integer>(Integer.class));
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
切面類
/**
* 單用戶限流切面
*/
/**
* 單用戶限流切面
*/
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class RestrictAccessAspect {
private final RedisTemplate<String,Integer> redisTemplate;
private static Integer INIT_VALUE = 1;
/**
* 用戶登錄的令牌
*/
private static final String USER_TOKEN = "token";
@Around("@annotation(cn.juh.annocation.RestrictAccess)")
public Object RestrictAccessFrequency(ProceedingJoinPoint point) throws Throwable {
//獲取當(dāng)前線程的訪問對象
HttpServletRequest request = WebUtils.getHttpServletRequest();
//獲取訪問路徑
String requestURI = request.getRequestURI();
//獲取用戶token
String token = request.getParameter(USER_TOKEN);
//存在redis中的key
String key = RedisConstant.KeyPrefix.RESTRICT_ACCESS.code() + requestURI + ":" + token;
MethodSignature sign = (MethodSignature) point.getSignature();
Method method = sign.getMethod();
//獲取方法上的注解
RestrictAccess annotation = method.getAnnotation(RestrictAccess.class);
int accessFrequency = annotation.accessFrequency();
long ttl = annotation.ttl();
//從redis中獲取用戶再限定時間內(nèi)訪問接口的次數(shù)
Integer value = redisTemplate.opsForValue().get(key);
if (Objects.nonNull(value) && value >= accessFrequency){
return "您的操作過于頻繁苗膝,請待會再試";
}
if (Objects.isNull(value)){
//不存在key則設(shè)置初始值并且設(shè)置過期時間
redisTemplate.opsForValue().set(key,INIT_VALUE,ttl, TimeUnit.MILLISECONDS);
}else {
//存在則將訪問次數(shù)+1
redisTemplate.opsForValue().increment(key);
}
//執(zhí)行接口邏輯
return point.proceed();
}
}
獲取web對象工具
public class WebUtils {
public static ServletRequestAttributes getServletRequestAttributes() {
return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
}
/**
* 得到當(dāng)前線程的請求對象
*
* @return
*/
public static HttpServletRequest getHttpServletRequest() {
return getServletRequestAttributes().getRequest();
}
/**
* 得到當(dāng)前線程的響應(yīng)對象
*
* @return
*/
public static HttpServletResponse getHttpServletResponse() {
return getServletRequestAttributes().getResponse();
}
}
測試接口
/**
* 控制器
*/
@RestController
public class TestController {
@RequestMapping("/test")
@RestrictAccess(ttl = 10000,accessFrequency = 1)
public Object first(String token) {
return "test";
}
}
正常訪問:
限流:
-
代碼邏輯