当前位置: 首页 > wzjs >正文

网站设计深圳网站建设公司近一周新闻热点事件

网站设计深圳网站建设公司,近一周新闻热点事件,html5网站建设基本流程,贵州公司网站建设文章目录 一、问题场景还原二、解决方案设计2.1 技术选型对比2.2 核心实现逻辑2.3 SpEL表达式 三、代码实现(SpringBoot 3.X MyBatisPlus AOP Redis)3.1 添加核心依赖3.2 定义防重注解3.3 实现AOP切面3.4 业务层使用示例场景1:用户注册&am…

文章目录

    • 一、问题场景还原
    • 二、解决方案设计
      • 2.1 技术选型对比
      • 2.2 核心实现逻辑
      • 2.3 SpEL表达式
    • 三、代码实现(SpringBoot 3.X + MyBatisPlus + AOP + Redis)
      • 3.1 添加核心依赖
      • 3.2 定义防重注解
      • 3.3 实现AOP切面
      • 3.4 业务层使用示例
        • 场景1:用户注册(仅依赖 手机号)
        • 场景2:用户提交当日运动计划(仅依赖 `userId` )
        • 场景3:用户提交订单(组合 `userId` 和参数)
    • 四、方案优势
    • 五、注意事项

引言:本文针对SpringBoot+MyBatisPlus项目中重复提交问题,提出基于动态Key+分布式锁的通用解决方案。通过AOP切面实现防重逻辑与业务解耦,支持灵活配置唯一键规则,日均节省无效请求30%+,适用于注册、下单、评论等高频场景。

一、问题场景还原

典型问题场景

  1. 用户注册接口连续点击
  2. 运动计划重复提交
  3. 订单创建高频请求
  4. 网络延迟导致连续触发多次请求
  5. 服务端处理耗时过长,前序请求未完成时新请求到达
  6. 恶意用户通过脚本高频调用接口

致命后果:数据库产生重复用户记录、库存超卖、积分重复发放等生产事故。

传统方案缺陷

  • 数据库唯一索引:无法应对动态组合键
  • 前端防抖:无法防御绕过浏览器的请求
  • synchronized锁:分布式环境失效

二、解决方案设计

2.1 技术选型对比

方案适用场景缺点
前端按钮防抖简单场景无法防御脚本攻击
数据库唯一索引写操作场景增加数据库压力
Token机制表单提交需要前后端配合
synchronized锁所有写接口分布式环境失效
Redis+AOP所有写接口需处理Redis故障

最终方案:采用Redis作分布式锁,AOP实现业务零侵入,支持动态Key生成

2.2 核心实现逻辑

技术栈组合

  • Spring AOP:实现业务无侵入
  • Redis分布式锁:保证集群环境一致性
  • SpEL表达式:支持动态Key生成

核心流程图

用户 AOP切面 Redis 业务代码 发起请求 生成唯一Key(用户ID+参数MD5) 返回是否存在 返回"请勿重复提交" 设置Key(5秒过期) 执行核心逻辑 返回结果 删除Key(仅当成功时) alt [Key已存在] [Key不存在] 用户 AOP切面 Redis 业务代码

2.3 SpEL表达式

SpEL(Spring Expression Language)是Spring框架的核心技术之一,是一种功能强大的表达式语言,支持在运行时动态查询和操作对象图。其语法简洁灵活,与Spring生态系统深度集成,广泛应用于配置、数据绑定、方法调用等场景。

SpEL通过灵活的语法和强大的运行时能力,显著提升了Spring应用的动态性和可配置性。其核心优势包括:

  • 简化复杂操作:通过表达式替代硬编码,减少冗余代码。
  • 动态适配:在配置、权限、数据绑定等场景中实现运行时决策。
  • 安全性平衡:通过上下文控制兼顾功能与安全。

三、代码实现(SpringBoot 3.X + MyBatisPlus + AOP + Redis)

3.1 添加核心依赖

<!-- 必须组件 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

3.2 定义防重注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicate {/*** 唯一Key的生成策略参数(支持SpEL表达式)* 示例:从参数中取手机号 -> #request.mobile* 从用户ID生成 -> #userId*/String key() default "";/*** 锁过期时间(默认3秒)*/int expire() default 3;/*** 错误提示信息*/String message() default "请勿重复提交";
}

3.3 实现AOP切面

import com.example.demo.annotation.PreventDuplicate;
import com.example.demo.config.result.ResultCode;
import com.example.demo.exception.base.BaseException;
import com.example.demo.uitls.UserHelper;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.core.ParameterNameDiscoverer;import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;/*** PreventDuplicateAspect : 防止重复提交切面** @author zyw* @create 2025-03-03  15:50*/@Aspect
@Component
public class PreventDuplicateAspect {@Resourceprivate StringRedisTemplate stringRedisTemplate;/***  获取方法参数名*/private static final ParameterNameDiscoverer PARAM_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();/*** 获取方法参数名* @param method* @return*/private String[] getParameterNames(Method method) {return PARAM_NAME_DISCOVERER.getParameterNames(method);}@Around("@annotation(prevent)")public Object checkDuplicate(ProceedingJoinPoint joinPoint, PreventDuplicate prevent) throws Throwable {// 1. 解析SpEL表达式生成唯一KeyString uniqueKey = generateUniqueKey(joinPoint, prevent.key());String lockKey = "prevent:submit:" + uniqueKey;// 2. 尝试获取分布式锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", prevent.expire(), TimeUnit.SECONDS);if (Boolean.FALSE.equals(success)) {throw new BaseException(ResultCode.REPEAT_SUBMIT, prevent.message());}try {// 3. 执行业务逻辑return joinPoint.proceed();} finally {// 4. 业务完成后删除Key(根据业务需求决定是否立即释放)stringRedisTemplate.delete(lockKey);}}/*** 解析SpEL表达式生成动态Key* @param joinPoint* @param keyExpression* @return*/private String generateUniqueKey(ProceedingJoinPoint joinPoint, String keyExpression) {// 1. 如果表达式为空,默认生成类+方法+参数哈希的Key(确保基本唯一性)if (keyExpression == null || keyExpression.isEmpty()) {return defaultKey(joinPoint);}// 2. 获取方法签名和参数值MethodSignature signature = (MethodSignature) joinPoint.getSignature();Object[] args = joinPoint.getArgs();String[] parameterNames = getParameterNames(signature.getMethod());// 3. 创建SpEL解析上下文,绑定参数名和值EvaluationContext context = new StandardEvaluationContext();for (int i = 0; i < args.length; i++) {context.setVariable(parameterNames[i], args[i]);}// 注入缓存中的用户IdLong userId = UserHelper.getLoginUserId();// 绑定到上下文变量context.setVariable("userId", userId);// 4. 解析表达式SpelExpressionParser parser = new SpelExpressionParser();Expression expression = parser.parseExpression(keyExpression);Object value = expression.getValue(context);// 5. 确保解析结果非空if (value == null) {throw new IllegalArgumentException("SpEL表达式解析结果为空: " + keyExpression);}// 6. 组合类名+方法名+表达式值生成唯一Key(避免不同接口冲突)String className = joinPoint.getTarget().getClass().getName();String methodName = signature.getMethod().getName();return String.format("lock:%s:%s:%s", className, methodName, value);}/***  默认Key生成策略:类名+方法名+参数哈希* @param joinPoint* @return*/private String defaultKey(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();String className = joinPoint.getTarget().getClass().getName();String methodName = signature.getMethod().getName();int paramsHash = Objects.hash(joinPoint.getArgs());return String.format("lock:%s:%s:%d", className, methodName, paramsHash);}
}

3.4 业务层使用示例

场景1:用户注册(仅依赖 手机号)
    @PreventDuplicate(key = "#dto.phone", expire = 10, message = "该手机号正在注册中,请勿重复提交")public Boolean register(RegistrationDto dto) {Long loginUserId = UserHelper.getLoginUserId();log.info("当前账号id:{},开始注册", loginUserId);// 模拟业务执行try {Thread.sleep(3000);}catch (Exception e){e.getStackTrace();}log.info("当前账号id:{},注册成功", loginUserId);return true;}
场景2:用户提交当日运动计划(仅依赖 userId
    @PreventDuplicate(key = "#userId", expire = 10, message = "该账号正在评论中,请勿重复评论")public Boolean submitComment(CommentDto dto) {Long loginUserId = UserHelper.getLoginUserId();log.info("当前账号id:{},开始评论", loginUserId);// 模拟业务执行try {Thread.sleep(3000);}catch (Exception e){e.getStackTrace();}log.info("当前账号id:{},评论成功", loginUserId);return true;}
场景3:用户提交订单(组合 userId 和参数)
    @PreventDuplicate(key = "#userId + '-' + #dto.productId", expire = 10, message = "该商品订单正在生成中,请勿重复提交")public Boolean submitOrder(OrderDto dto) {Long loginUserId = UserHelper.getLoginUserId();log.info("当前账号id:{},开始提交订单", loginUserId);// 模拟业务执行try {Thread.sleep(3000);}catch (Exception e){e.getStackTrace();}log.info("当前账号id:{},订单提交成功", loginUserId);return true;}

在这里插入图片描述

四、方案优势

  1. 动态Key生成:支持用户ID、手机号、设备ID等多种组合方式
  2. 分布式生效:Redis集群保证多实例环境下的防重一致性
  3. 性能优异:Redis操作耗时<3ms,远低于数据库唯一约束方案
  4. 灵活配置:通过interval参数控制防重时间窗口(秒级精度)
  5. 故障容错:Redis宕机时可通过@ConditionalOnBean降级处理
维度本方案数据库唯一索引本地锁
分布式支持
动态Key
性能影响<1ms依赖索引性能纳秒级
代码侵入性
异常处理自动释放锁依赖事务回滚易死锁

五、注意事项

  1. Key设计原则:建议包含「业务类型+唯一标识」,如REGISTER:13800138000
  2. 过期时间:根据业务耗时设置,建议「平均处理时间*3」
  3. 异常处理:在finally块中根据业务结果决定是否立即删除Key
  4. 压力测试:建议用JMeter模拟1000+并发验证防重效果
http://www.dtcms.com/wzjs/246877.html

相关文章:

  • 沈阳市绿云网站建设网络上哪里可以做推广
  • 做淘宝要网站?查询域名网站
  • 网站建设服务合同建网站模板
  • 设计本app苏州搜索引擎优化
  • 天津高端网站建设石家庄百度搜索优化
  • 邯郸网站建设的企业友情链接交易网
  • 企业网站建设论文网络整合营销方案ppt
  • 网站建设 客户seo公司重庆
  • 甘肃庆阳疫情最新消息推广学院seo教程
  • 手机网站用什么制作比较靠谱的电商培训机构
  • 什么网站能通过做任务赚钱吗策划公司排行榜
  • 铜陵做网站的站长工具seo综合查询怎么使用的
  • php源码搭建网站流程武汉网站维护公司
  • 全国新冠新增最新消息seo小白入门
  • 开源众包河南网站排名优化
  • 怎么在虚拟空间做两个网站常用搜索引擎有哪些
  • 中医科网站建设素材短链接生成网址
  • 北京网站建设价位关键词seo
  • 宜昌c2b网站建设临汾网络推广
  • 南通政府门户网站建设企业管理培训班哪个好
  • 建设一个企业网站到底要多少钱seo优化快排
  • 网站建设策划基本流程好的竞价托管公司
  • 摄影作品网站app十大排名推广排名
  • 深圳住房和建设局网站 申请深圳网页设计公司
  • 比较厉害的网站制作公司快刷网站
  • 网站建站网站哪家好石家庄网站建设排名
  • 什么网站可以帮人做ppt赚钱爱站网查询
  • 商城网站建设流程关键词自动生成器
  • 河北中尊建设工程有限公司官方网站网络推广有效果吗
  • 自己做网站需要服务器seo和sem的区别与联系