SSM(十四) 基于 annotation 的 http 防重插件(上)

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 针对于我们现在常用的RESTful API通常我们需要对请求进行唯一标识,也就是每次都要带上一个请求号,如reqNO。

前言


针对于我们现在常用的RESTful API通常我们需要对请求进行唯一标识,也就是每次都要带上一个请求号,如reqNO


对于入库这种操作数据库的请求我们一般要保证他的唯一性,一个请求号通常只能用一次,所以需要我们对这种请求加上校验机制。


该需求的实现思路是通过自定义annotation,只给需要进行校验的接口加上注解。然后通过切面使用了注解的接口将每次请求号存进Redis,每次都进行判断是否存在这个请求号即可。


来看下加上本次插件的实际效果:


重复请求号01.jpg


重复请求号02.jpg


重复请求号03.jpg


自定义注解


首先我们要自定义一个注解:


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckReqNo {
    String desc() default "";
}


(ps:这里并不过多的讲解注解相关的知识)。


首先使用@interface来声明一个注解。接着利用Java为我们提供的三个元注解来定义CheckReqNo注解。


其中@Target表明这个注解被用于什么地方,使用ElementType.METHOD表明被应用到方法上,还有一些其他值可以查看java.lang.annotation.ElementType这个枚举类型。


@Retention注解表明我们的注解在什么范围内有效,这里配置的RetentionPolicy.RUNTIME表明在运行时可以通过反射来获取。


@Documented看字面意思应该也能猜到是用于生成JavaDoc文档的。


其中定义了一个desc()的方法其实并没有用到,但如果需要在使用注解的时候需要自定义一些filed(域)的需求可以按照这样的方式写到这里,通过反射都可以获取到具体的值。


如:@CheckReqNo(desc = "abc")就可以获取到"abc"的值。


切面注解


按照之前的想法是在对所有使用了该注解的方法进行切面:


@Aspect
@Component
public class ReqNoDrcAspect {
    private static Logger logger = LoggerFactory.getLogger(ReqNoDrcAspect.class);
    @Value("${redis.prefixReq:reqNo}")
    private String prefixReq ;
    @Value("${redis.day:1}")
    private long day ;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @PostConstruct
    public void init() throws Exception {
        logger.info("SSM-REQUEST-CHECK init......");
    }
    @Pointcut("@annotation(com.crossoverJie.request.anotation.CheckReqNo)")
    public void checkRepeat(){
    }
    @Before("checkRepeat()")
    public void before(JoinPoint joinPoint) throws Exception {
        BaseRequest request;
        request = getBaseRequest(joinPoint);
        if(request != null){
            final String reqNo = request.getReqNo();
            if(StringUtil.isEmpty(reqNo)){
                throw new RuntimeException("reqNo不能为空");
            }else{
                try {
                    String tempReqNo = redisTemplate.opsForValue().get(prefixReq +reqNo);
                    logger.debug("tempReqNo="+tempReqNo);
                    if((StringUtil.isEmpty(tempReqNo))){
                        redisTemplate.opsForValue().set(prefixReq + reqNo, reqNo, day, TimeUnit.DAYS);
                    }else{
                        throw new RuntimeException("请求号重复,reqNo="+reqNo);
                    }
                } catch (RedisConnectionFailureException e){
                    logger.error("redis操作异常",e);
                    throw new RuntimeException("need redisService") ;
                }
            }
        }
    }
     public static BaseRequest getBaseRequest(JoinPoint joinPoint) throws Exception {
         BaseRequest returnRequest = null;
         Object[] arguments = joinPoint.getArgs();
         if(arguments != null && arguments.length > 0){
             returnRequest = (BaseRequest) arguments[0];
         }
         return returnRequest;
     }
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
12月前
UE Http Server 插件说明
UE Http Server 插件说明
295 0
|
12月前
|
XML JSON JavaScript
UE4 HTTP 请求插件说明(DTHttpRequest)
UE4 HTTP 请求插件说明(DTHttpRequest)
296 0
|
测试技术
JMeter http(s)请求插件jmeter-plugin-httpBinaryFileUpload.jar
JMeter http(s)请求插件jmeter-plugin-httpBinaryFileUpload.jar
99 0
|
Web App开发 存储 前端开发
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
1.HBase依赖于HDFS,HBase按照列族将数据存储在不同的hdfs文件中;MongoDB直接存储在本地磁盘中,MongoDB不分列,整个文档都存储在一个(或者说一组)文件中 (存储) 2.
700 0
|
Web App开发 Apache
|
Web App开发 前端开发 Java
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html><head><meta http-equiv="Cont
 Connection reset by peer的常见原因: 1)服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭;    如果知道实际连接服务器的并发客户数没有超过服务器的承载量,看下有没有网络流量异常。
836 0