网站建设的实训心得,英雄联盟更新公告最新,学习网站模板,系统开发费本文项目基于以下教程的代码版本#xff1a; https://javaxbfs.blog.csdn.net/article/details/135224261
代码仓库: springboot一些案例的整合_1: springboot一些案例的整合 1、实现步骤 2.引入依赖
我们需要redis、aop的依赖。
dependencygroupIdorg.spr…本文项目基于以下教程的代码版本 https://javaxbfs.blog.csdn.net/article/details/135224261
代码仓库: springboot一些案例的整合_1: springboot一些案例的整合 1、实现步骤 2.引入依赖
我们需要redis、aop的依赖。
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjrt/artifactIdversion1.9.6/version
/dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactIdversion1.9.6/version
/dependencyredis的访问参数默认的就行不需要特别配置。
3、定义拦截注解
我们最终希望的效果是你想要哪个方法有防止重复提交的功能直接加上RepeatSubmit注解即可。 代码如下
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface RepeatSubmit {/*** 加锁过期时间默认是5秒* return*/long lockTime() default 5L;
}这段代码定义了一个Java注解Annotation叫做RepeatSubmit。注解是Java提供的一种元数据机制它可以被用于为代码提供附加的信息这些信息可以被编译器用于生成代码、生成文档、代码检查等。
下面是对这段代码的详细解释 Target(ElementType.METHOD): 这个注解指定RepeatSubmit只能被用于方法上。ElementType.METHOD表示这个注解只能用于方法。 Retention(RetentionPolicy.RUNTIME): 这个注解指定了RepeatSubmit的保留策略是RUNTIME。这意味着在运行时这个注解仍然可以被读取。 Documented: 这个注解表明RepeatSubmit应当被Java文档生成器包含在生成文档中。 public interface RepeatSubmit: 定义了一个名为RepeatSubmit的公共注解。 long lockTime() default 5L: 这是RepeatSubmit注解的一个元素名为lockTime。这个元素返回一个长整型值并且有一个默认值5秒5L表示5秒。
这个注解是为了防止方法的重复提交并提供了一个默认的加锁过期时间为5秒。如果一个方法被这个注解标记那么在特定的加锁过期时间内这个方法只会被执行一次。
4、实现防止重复提交切面
关于Spring Aop切面可以看我之前的文章。
NoRepeatSubmitAspect
package com.it.demo.aspect;import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.it.demo.annotation.RepeatSubmit;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.TimeUnit;Component
Slf4j
Aspect
public class NoRepeatSubmitAspect {Resourceprivate RedisTemplate redisTemplate;Pointcut(annotation(repeatSubmit))public void pointCutNoRepeatSubmitAspect(RepeatSubmit repeatSubmit) {}Around(pointCutNoRepeatSubmitAspect(repeatSubmit))public Object around(ProceedingJoinPoint joinPoint,RepeatSubmit repeatSubmit) throws Throwable {String reqSignature buildReqSignature(joinPoint);//加锁时间long lockTime repeatSubmit.lockTime();Boolean res redisTemplate.opsForValue().setIfAbsent(reqSignature, 1, lockTime, TimeUnit.SECONDS);if(!res){throw new RuntimeException(重复请求);}return joinPoint.proceed();}/*** 构建请求签名* param joinPoint* return*/private String buildReqSignature(ProceedingJoinPoint joinPoint) {ServletRequestAttributes requestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(Objects.isNull(requestAttributes)){throw new RuntimeException(请求参数异常);}HttpServletRequest request requestAttributes.getRequest();String remoteAddr request.getRemoteAddr() : request.getRemoteHost();String method request.getMethod();String requestURI request.getRequestURI();StringBuffer sb new StringBuffer();for (Object arg : joinPoint.getArgs()) {sb.append(arg);}//请求签名(MD5加密)return DigestUtil.md5Hex(StrUtil.format({}-{}-{}-{},remoteAddr,method,requestURI,sb));}
}
这段代码是一个切面Aspect用于处理重复提交的情况。以下是对代码中的主要部分的解释 导入包及注解代码首先导入了一些类并使用了一些注解例如 Component, Aspect, Resource, Slf4j 等。 NoRepeatSubmitAspect 类这是一个切面类用于实现对重复提交的处理逻辑。 成员变量代码中使用了 Resource 注解注入了一个 RedisTemplate 对象用于操作 Redis。 切入点使用 Pointcut 注解定义了一个切入点这里使用了 annotation(repeatSubmit) 表达式表示会拦截所有标注了 RepeatSubmit 注解的方法。 Around 通知使用 Around 注解定义了一个环绕通知在拦截的方法执行前后会执行这里定义的逻辑。 around() 方法在该方法中首先调用了 buildReqSignature(joinPoint) 方法构建了请求的签名信息然后根据 RepeatSubmit 注解中的配置在 Redis 中设置了一个锁以防止重复提交。如果设置锁失败则抛出了一个运行时异常表示重复请求否则执行被拦截的方法。 buildReqSignature() 方法该方法用于构建请求的签名信息首先获取了请求相关的信息然后将这些信息进行拼接并使用 MD5 加密生成请求签名。
这段代码是一个使用 AOP 实现的防止重复提交的切面在方法执行前通过生成请求签名并利用 Redis 进行锁定的方式来避免重复提交。
环绕通知实现逻辑如下图所示 构建 Redis 加锁需要使用的 key加锁key由指定前缀 MD5(请求签名)组成对请求签名进行MD5一方面减少Key的长度另一方面保证签名信息中的敏感信息不可见其实现逻辑如下图所示
/*** 构建请求签名* param joinPoint* return*/
private String buildReqSignature(ProceedingJoinPoint joinPoint) {ServletRequestAttributes requestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if(Objects.isNull(requestAttributes)){throw new RuntimeException(请求参数异常);}HttpServletRequest request requestAttributes.getRequest();String remoteAddr request.getRemoteAddr() : request.getRemoteHost();String method request.getMethod();String requestURI request.getRequestURI();StringBuffer sb new StringBuffer();for (Object arg : joinPoint.getArgs()) {sb.append(arg);}//请求签名(MD5加密)return DigestUtil.md5Hex(StrUtil.format({}-{}-{}-{},remoteAddr,method,requestURI,sb));
}5、 测试 查询方法加一个RepeatSubmit
GetMapping(/list)
RepeatSubmit
public ListDevice list(){ListDevice list deviceService.list();System.out.println(list);return deviceService.list();
}访问localhost:8080/v1/device/list 五秒内再访问报错
{code: 400,msg: 重复请求,data: null
}验证通过。