做JavaWeb的开发的同学们都应该遇到过,客户要求某个接口进行频次的限制,如每秒并发10个,或者短信验证码发送场景,60秒内只允许发送一次。
通常开发的小伙伴们肯定是拿到以上需求在接口逻辑里进行实现,噼里啪啦一顿输出,OK搞定了,很简单。的确,这些需求比较简单,但是,有没有想过,这种频次限制其实是和业务耦合度不高,能不能统一封装,在业务需要的时候直接设置就行了呢?
答案是肯定的。自定义注解来实现就非常的方便,对你的业务接口增加自定义注解,无需修改业务代码即可快速按需完成想要的功能。
第一步:当然是定义一个自定义注解,如RateLimit
![8751d2d138080773551ec37205b09bb9.png](https://i-blog.csdnimg.cn/blog_migrate/3cc969c211214db1b54ff3f2f7fd1226.jpeg)
定义一个自定义注解RateLimit应用在方法或者类上,并定义相关关键字段,参数Key、频次数、时间、错误提示等。意思就是,在多少时间内,根据参数Key获取的值为判断依据,允许多少频次数的访问,如果超过,那么提示错误提示语,并拒绝访问。
第二步:实现HandlerInterceptor自定义一个拦截器
![0a8a5093c97b600ae89904ab4152a2d1.png](https://i-blog.csdnimg.cn/blog_migrate/7146c42e17cf99b750ab0fb691a038f9.jpeg)
在拦截器里重写preHandle,将Object handler转换成HandlerMethod类型传递给自定义的securityManager处理,小伙伴也可以在这个地方直接实现,直接实现代码就不是显得那么高大上了。
第三步:核心处理逻辑
![bcd4a0827d06b6f38b7e8cb370c69b8c.png](https://i-blog.csdnimg.cn/blog_migrate/ff7486251be92112c0169dfe1346108e.jpeg)
![528099cf24071d8195f5eec147b3ed01.png](https://i-blog.csdnimg.cn/blog_migrate/2a339044a40adb5a568a915404b8d559.jpeg)
![059a341a41bc7a3bba9a3db8352708b9.png](https://i-blog.csdnimg.cn/blog_migrate/220611a349107ed010f77b7a06bc4f84.jpeg)
![efb65b666df05ca8a3b1d4ee52abbeec.png](https://i-blog.csdnimg.cn/blog_migrate/694815fa4f602af112d4d8473efedd34.jpeg)
核心处理方法为handle方法,专门处理类RateLimitHandler。该方法,获取到方法上的注解类RateLimit,并获取到注解类里的关键信息:key()、fromHead()。fromHead为true表示从请求头根据key值获取对应的value值作为唯一标识uuid,否则直接从请求参数里根据key获取唯一标识。获取唯一标识后,调用validateRate方法进行校验是否超过频次,传入uuid,时间、时间单元,当前请求对象。
![7cd0889136c782580dbec6a99a421293.png](https://i-blog.csdnimg.cn/blog_migrate/aecccc5f870b7fa658877eefc52ce773.jpeg)
校验方法,首先根据uuid生成一个内部key,用于区分不同业务,cacheHandler是一个缓存实现,本例中,cacheHandler其实注入的是redis实例,也可以是内存实例。调用cacheHandler自增方法返回一个值,为什么要用自增,是因为考虑并发情况,不能先get再set。如果为1,表示第一次访问,设置一个失效时间,即注解定义的多少时间内,在这个时间过后,自动失效。
最后判断次数是否超过注解类定义的频次数,如果超过,返回false,否则,返回true。到此,自定义注解的实现就结束了。大家疑惑,如果使用起来呢。
第四步:使用,如:60秒内,只允许请求一次(哈哈哈短信验证码使用场景)
![e930f396e1e43bf3130a08958589c951.png](https://i-blog.csdnimg.cn/blog_migrate/157e03fa1d79b3ce92cefa5bc644cae0.jpeg)
OK,就是这么简单,后续有其他相似的业务只需加这个注解,修改不同的时间、限制次数就可以。