自定义注解和使用
1、使用@interface关键定义注解(RateLimiter.java),如下:
package com.vx.servicehi.annotation; import java.lang.annotation.*; /** * @author wangbs * @version 1.0 * @date 2019/12/16 1:25 * @className RateLimiter * @desc 限流注解 */ //注解作用域 // ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上 // ElementType.FIELD:允许作用在属性字段上 // ElementType.METHOD:允许作用在方法上 // ElementType.PARAMETER:允许作用在方法参数上 // ElementType.CONSTRUCTOR:允许作用在构造器上 // ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上 // ElementType.ANNOTATION_TYPE:允许作用在注解上 // ElementType.PACKAGE:允许作用在包上 // // 注解的生命周期 // RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件 // RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件 // RetentionPolicy.RUNTIME:永久保存,可以反射获取 // 注解的作用域 @Target(ElementType.METHOD) // 注解的生命周期 @Retention(RetentionPolicy.RUNTIME) // 允许子类继承 @Inherited // 生成javadoc的时候生成注解的信息 @Documented public @interface RateLimiter { /** * 限流key * @return */ String key() default "rate:limiter"; /** * 单位时间限制通过请求数 * @return */ long limit() default 10; /** * 过期时间,单位秒 * @return */ long expire() default 1; /** * 限流提示语 * @return */ String message() default "false"; }
2、在普通类上使用注解,使用方法
package com.vx.servicehi.controller;
import com.vx.servicehi.annotation.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("business")
public class BusinessController {
private static final Logger LOGGER = LoggerFactory.getLogger(BusinessController.class);
private static final String MESSAGE = "{\"code\":\"400\",\"msg\":\"FAIL\",\"desc\":\"触发限流\"}";
@Value("${server.port}")
String port;
/**
* 使用自定义注解 限流
* @param name
* @return
*/
@RequestMapping("/hi")
@RateLimiter(key = "business/hi", limit = 5, expire = 10, message = MESSAGE)
public String home(@RequestParam(value = "name", defaultValue = "forezp") String name) {
return "hi " + name + " ,i am from port:" + port;
}
}
3. 定义切面
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102package
com.vx.servicehi.handler;
import
com.vx.servicehi.annotation.RateLimiter;
import
org.apache.commons.lang3.StringUtils;
import
org.aspectj.lang.ProceedingJoinPoint;
import
org.aspectj.lang.Signature;
import
org.aspectj.lang.annotation.Around;
import
org.aspectj.lang.annotation.Aspect;
import
org.aspectj.lang.annotation.Pointcut;
import
org.aspectj.lang.reflect.MethodSignature;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.core.io.ClassPathResource;
import
org.springframework.data.redis.core.RedisTemplate;
import
org.springframework.data.redis.core.script.DefaultRedisScript;
import
org.springframework.scripting.support.ResourceScriptSource;
import
org.springframework.stereotype.Component;
import
javax.annotation.PostConstruct;
import
java.util.ArrayList;
import
java.util.List;
/**
* @author wangbs
* @version 1.0
* @date 2019/12/16 1:17
* @className RateLimterHandler
* @desc 限流处理器
*/
@Aspect
@Component
public
class
RateLimterHandler {
private
static
final
Logger LOGGER = LoggerFactory.getLogger(RateLimterHandler.
class
);
@Autowired
RedisTemplate redisTemplate;
private
DefaultRedisScript<Long> getRedisScript;
@PostConstruct
public
void
init() {
getRedisScript =
new
DefaultRedisScript<>();
getRedisScript.setResultType(Long.
class
);
getRedisScript.setScriptSource(
new
ResourceScriptSource(
new
ClassPathResource(
"rateLimter.lua"
)));
LOGGER.info(
"RateLimterHandler[分布式限流处理器]脚本加载完成"
);
}
@Pointcut
(
"@annotation(com.vx.servicehi.annotation.RateLimiter)"
)
public
void
rateLimiter() {}
@Around
(
"@annotation(rateLimiter)"
)
public
Object around(ProceedingJoinPoint proceedingJoinPoint, RateLimiter rateLimiter)
throws
Throwable {
if
(LOGGER.isDebugEnabled()) {
LOGGER.debug(
"RateLimterHandler[分布式限流处理器]开始执行限流操作"
);
}
Signature signature = proceedingJoinPoint.getSignature();
if
(!(signature
instanceof
MethodSignature)) {
throw
new
IllegalArgumentException(
"the Annotation @RateLimter must used on method!"
);
}
/**
* 获取注解参数
*/
// 限流模块key
String limitKey = rateLimiter.key();
if
(StringUtils.isBlank(limitKey)){
throw
new
NullPointerException();
}
// 限流阈值
long
limitTimes = rateLimiter.limit();
// 限流超时时间
long
expireTime = rateLimiter.expire();
if
(LOGGER.isDebugEnabled()) {
LOGGER.debug(
"RateLimterHandler[分布式限流处理器]参数值为-limitTimes={},limitTimeout={}"
, limitTimes, expireTime);
}
// 限流提示语
String message = rateLimiter.message();
if
(StringUtils.isBlank(message)) {
message =
"false"
;
}
/**
* 执行Lua脚本
*/
List<String> keyList =
new
ArrayList();
// 设置key值为注解中的值
keyList.add(limitKey);
/**
* 调用脚本并执行
*/
Long result = (Long) redisTemplate.execute(getRedisScript, keyList, expireTime, limitTimes);
if
(result ==
0
) {
String msg =
"由于超过单位时间="
+ expireTime +
"-允许的请求次数="
+ limitTimes +
"[触发限流]"
;
LOGGER.debug(msg);
return
message;
}
if
(LOGGER.isDebugEnabled()) {
LOGGER.debug(
"RateLimterHandler[分布式限流处理器]限流执行结果-result={},请求[正常]响应"
, result);
}
return
proceedingJoinPoint.proceed();
}
}
4. 在resource 下定义文件 rateLimter.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | --获取KEY local key1 = KEYS[ 1 ] --给指定的key 值增加一,如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作 local val = redis.call( 'incr' , key1) --以秒为单位返回 key 的剩余过期时间 local ttl = redis.call( 'ttl' , key1) --获取ARGV内的参数并打印 local expire = ARGV[ 1 ] local times = ARGV[ 2 ] redis.log(redis.LOG_DEBUG,tostring(times)) redis.log(redis.LOG_DEBUG,tostring(expire)) redis.log(redis.LOG_NOTICE, "incr " ..key1.. " " ..val); if val == 1 then redis.call( 'expire' , key1, tonumber(expire)) else if ttl == - 1 then --expire当key不存在或者不能为key设置生存时间时返回 0 redis.call( 'expire' , key1, tonumber(expire)) end end if val > tonumber(times) then return 0 end return 1 |
5、解析注解,通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
package com.vx.servicehi.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 解析注解
* 通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
*
* 对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
getAnnotation:返回指定的注解
isAnnotationPresent:判定当前元素是否被指定注解修饰
getAnnotations:返回所有的注解
getDeclaredAnnotation:返回本元素的指定注解
getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的
*
* @author wangbs
* @date 2019-12-21 22:52:42
* 主要是对自定义注解 RateLimiter 的获取 测试
*
*/
public class ParseDecription {
public static void main(String[] args) {
// TODO Auto-generated method stub
// 1、使用类加载器加载类
try {
Class c = Class.forName("com.vx.servicehi.controller.BusinessController");
System.out.println(c);
// 2、找到类上面的注解
boolean isExist = c.isAnnotationPresent(RateLimiter.class);
if(isExist) {
// 3、拿到注解实例
RateLimiter d = (RateLimiter) c.getAnnotation(RateLimiter.class);
System.out.println("========parse class annotation=========");
System.out.println("desc = " + d.key());
System.out.println("author = " + d.message());
System.out.println("age = " + d.limit());
}
// 4、找到方法上的注解
Method[] ms = c.getMethods();
for (Method m : ms) {
boolean isMExist = m.isAnnotationPresent(RateLimiter.class);
if(isMExist) {
RateLimiter d = m.getAnnotation(RateLimiter.class);
System.out.println("========parse method annotation=========");
System.out.println("desc = " + d.key());
System.out.println("author = " + d.message());
System.out.println("age = " + d.limit());
}
}
// 另外一种解析方法
for (Method m : ms) {
Annotation[] annotations = m.getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof RateLimiter) {
System.out.println("========parse method annotation other way=========");
RateLimiter d = (RateLimiter) annotation;
System.out.println("desc = " + d.key());
System.out.println("author = " + d.message());
System.out.println("age = " + d.limit());
}
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构