- Redis简介
- 使用内存存储,使用单线程,采用IO多路复用模型,性能极好
- 支持String,Hash,List,Set,Zset等多种数据类型
- 支持key定时失效
- 可采用AOF,RDB方式进行持久化
- AOP简介
- 定义:面向切面编程,将跟业务逻辑无关的重复并且萦绕在方法周围的代码进行抽取,提高了代码的可重用性
-
常用注解:
切面.PNG
- 注解简介
- 使用条件:定义注解,声明注解的生命周期、作用域,注解实现体
- 定义在自定义注解上的元注解:
@Target:注解的作用域,包含ElementType参数
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,//作用域在方法参数
/** Constructor declaration */
CONSTRUCTOR,//作用域在构造器
/** Local variable declaration */
LOCAL_VARIABLE,//作用域在局部变量
/** Annotation type declaration */
ANNOTATION_TYPE,//作用域在注解
/** Package declaration */
PACKAGE,//作用域在包
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,//作用域在类型参数
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE//作用域在使用类型的任何地方
}
@Retention:注解的作用域,包含RetentionPolicy参数
/**
* 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,//存活在字节码文件中
/**
* 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//存活在代码运行期间
@Inherited:允许子类继承父类的注解
@Documented:此注解会包含在javadoc中
-
代码实现
定义注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RestrictAccess {
/**
* 限制时间,单位是毫秒
*/
long ttl() default 0;
/**
* 限制时间内的访问次数
*/
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 {
//获取当前线程的访问对象
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中获取用户再限定时间内访问接口的次数
Integer value = redisTemplate.opsForValue().get(key);
if (Objects.nonNull(value) && value >= accessFrequency){
return "您的操作过于频繁,请待会再试";
}
if (Objects.isNull(value)){
//不存在key则设置初始值并且设置过期时间
redisTemplate.opsForValue().set(key,INIT_VALUE,ttl, TimeUnit.MILLISECONDS);
}else {
//存在则将访问次数+1
redisTemplate.opsForValue().increment(key);
}
//执行接口逻辑
return point.proceed();
}
}
获取web对象工具
public class WebUtils {
public static ServletRequestAttributes getServletRequestAttributes() {
return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
}
/**
* 得到当前线程的请求对象
*
* @return
*/
public static HttpServletRequest getHttpServletRequest() {
return getServletRequestAttributes().getRequest();
}
/**
* 得到当前线程的响应对象
*
* @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";
}
}
正常访问:
正常访问.PNG
限流:
限流.PNG
-
代码逻辑
程序执行逻辑.PNG