springboot aop实现接口防重复操作

这篇具有很好参考价值的文章主要介绍了springboot aop实现接口防重复操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、前言
有时在项目开发中某些接口逻辑比较复杂,响应时间长,那么可能导致重复提交问题。

二、如何解决
1.先定义一个防重复提交的注解。

import java.lang.annotation.*;

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {

    /**
     * 防重复操作限时标记数值(存储redis限时标记数值)
     */
    String value() default "value" ;

    /**
     * 防重复操作过期时间(借助redis实现限时控制)
     */
    int expireSeconds() default 10;
}

2.编写防重复操作的AOP

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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Slf4j
@Component
@Aspect
@Order(0)
public class NoRepeatSubmitAspect  {
 private static final String TOKENAuthorization = "Authorization";

    private static final String TOKENUSERNAME = "api-userName";

    private static final String PREVENT_DUPLICATION_PREFIX = "PREVENT_DUPLICATION_PREFIX:";

    @Autowired
    private RedisService redisService;

    @Pointcut("@annotation(com.dp.aop.annotation.RepeatSubmit)")
    public void preventDuplication() {}
    @Around("preventDuplication()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (Objects.isNull(request)) {
            return joinPoint.proceed();
        }
        //获取执行方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取防重复提交注解
        RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
        //获取token以及方法标记,生成redisKey
        String header = request.getHeader(TOKENAuthorization);
        String token = header == null ? "" : header;
        String requestHeader = request.getHeader(TOKENUSERNAME);
        String headerToken = requestHeader == null ? "" : requestHeader;
        token = token + headerToken;
        String url = request.getRequestURI();
        // 通过前缀 + url + token + 函数参数签名 来生成redis上的 key
        String redisKey = PREVENT_DUPLICATION_PREFIX
                .concat(url)
                .concat(token)
                .concat(getMethodSign(method, joinPoint.getArgs()));
        RedisLock redisLock = null;
        try {
            try {
                redisLock = redisService.tryLock(redisKey, annotation.expireSeconds());
            } catch (Exception e) {
                log.error("tryLock error  ", e);
                throw new BizException(CommonMsgConstants.NoRepeatSubmitMsg);
            }
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("throwable trace is ", throwable);
            throw new RuntimeException(throwable);
        } finally {
            if (Objects.nonNull(redisLock)) {
                redisLock.unlock();
            }
        }
    }
    /**
     * 生成方法标记:采用数字签名算法SHA1对方法签名字符串加签
     *
     * @param method
     * @param args
     * @return
     */
    private String getMethodSign(Method method, Object... args) {
        StringBuilder sb = new StringBuilder(method.toString());
        for (Object arg : args) {
            sb.append(toString(arg));
        }
        return DigestUtil.sha1Hex(sb.toString());
    }

    private String toString(Object arg) {
        if (Objects.isNull(arg)) {
            return "null";
        }
        if (arg instanceof Number) {
            return arg.toString();
        }
        return JSONObject.toJSONString(arg);
    }

}

3.接下来定义redisService类

@Component
public class RedisService {
	public RedisLock tryLock(String lockKey, int expireTime) {
        String lockValue = UUID.randomUUID().toString();
        Boolean hasLock = (Boolean)this.redisTemplate.execute((connection) -> {
            Object nativeConnection = connection.getNativeConnection();
            String status = null;
            if (nativeConnection instanceof Jedis) {
                Jedis jedis = (Jedis)nativeConnection;
                status = jedis.set(lockKey, lockValue, "nx", "ex", expireTime);
            } else {
                JedisCluster jedisx = (JedisCluster)nativeConnection;
                status = jedisx.set(lockKey, lockValue, "nx", "ex", (long)expireTime);
            }

            return "OK".equals(status);
        });
        if (hasLock) {
            return new RedisService.RedisLockInner(this.redisTemplate, lockKey, lockValue);
        } else {
            throw new RuntimeException("获取锁失败,lockKey:" + lockKey);
        }
    }
 private class RedisLockInner implements RedisLock {
        private RedisTemplate redisTemplate;
        private String key;
        private String expectedValue;

        protected RedisLockInner(RedisTemplate redisTemplate, String key, String expectedValue) {
            this.redisTemplate = redisTemplate;
            this.key = key;
            this.expectedValue = expectedValue;
        }

       public Object unlock() {
            final List<String> keys = new ArrayList();
            keys.add(this.key);
            final List<String> values = new ArrayList();
            values.add(this.expectedValue);
            Object result = this.redisTemplate.execute(new RedisCallback<Long>() {
                public Long doInRedis(RedisConnection connection) throws DataAccessException {
                    Object nativeConnection = connection.getNativeConnection();
                    return nativeConnection instanceof JedisCluster ? (Long)((JedisCluster)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values) : (Long)((Jedis)nativeConnection).eval("if redis.call('get',KEYS[1])==ARGV[1]\n then\n   return redis.call('del',KEYS[1])\n else\n   return 0\n end", keys, values);
                }
            });
           return result;
        }

        public void close() throws Exception {
            this.unlock();
        }
    }
}

4.最后在Controller接口加上注解就行了。文章来源地址https://www.toymoban.com/news/detail-669062.html

到了这里,关于springboot aop实现接口防重复操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot入门(23):基于AOP实现自定义注解拦截接口日志并保存入库 | 超级详细,建议收藏

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

    2024年02月11日
    浏览(35)
  • 【springboot】spring的Aop结合Redis实现对短信接口的限流

    场景: 为了限制短信验证码接口的访问次数,防止被刷,结合Aop和redis根据用户ip对用户限流 首先我们创建一个 Spring Boot 工程,引入 Web 和 Redis 依赖,同时考虑到接口限流一般是通过注解来标记,而注解是通过 AOP 来解析的,所以我们还需要加上 AOP 的依赖,最终的依赖如下:

    2024年02月05日
    浏览(39)
  • spring boot 使用AOP+自定义注解+反射实现操作日志记录修改前数据和修改后对比数据,并保存至日志表

    使用FieldMeta自定义注解,看个人业务自行觉得是否需要重新定义实体 实现类 :通过该实现类获取更新前后的数据。 该实现类的实现原理为:获取入参出入的id值,获取sqlSessionFactory,通过sqlSessionFactory获取selectByPrimaryKey()该方法,执行该方法可获取id对应数据更新操作前后的数

    2024年01月23日
    浏览(40)
  • Spring Boot + Aop 记录用户操作日志

    本文主要介绍通过Aop记录用户操作日志,这也是目前比较常用的用法,由于水平有限,所以可能存在错漏之处,望指正。 对应实体类为 SysOperLog.java MyLog.java BusinessType.java — 操作类型枚举类 LogAspect.java 用户操作日志是AOP最常见的一种业务场景,这里只是简单记录了少量信息,

    2024年02月06日
    浏览(38)
  • Spring Boot入门(23):记录接口日志再也不难!用AOP和自定义注解给Spring Boot加上日志拦截器!

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

    2024年02月11日
    浏览(44)
  • springboot3使用自定义注解+AOP+redis优雅实现防重复提交

      ⛰️个人主页:     蒾酒 🔥 系列专栏 :《spring boot实战》 🌊 山高路远,行路漫漫,终有归途 目录 写在前面 实现思路 实现步骤 1.定义防重复提交注解 2.编写一个切面去发现该注解然后执行防重复提交逻辑 3.测试 依赖条件 1.接口上标记防重复提交注解 2.接口测试 写在最

    2024年04月11日
    浏览(32)
  • 使用Spring Boot AOP实现日志记录

    目录 介绍 1.1 什么是AOP 1.2 AOP体系与概念 AOP简单实现 2.1 新建一个SpringBoot项目,无需选择依赖 2.2 设置好本地Maven配置后,在pom.xml文件里添加添加maven依赖 2.3 创建一个业务类接口 2.4 在实体类实现接口业务  2.5 在单元测试运行结果 2.6 创建切面类 2.7 再次运行测试  总结 1.

    2024年02月14日
    浏览(44)
  • spring boot 使用AOP实现是否已登录检测

            前后端分离的开发中,用户http请求应用服务的接口时, 如果要求检测该用户是否已登录。可以实现的方法有多种, 本示例是通过aop 的方式实现,简单有效。         约定:前端http的post 请求 1、在pom.xml 引用 2、创建插入标记 3、实现切入类 4 建立api接口,在需要检

    2024年02月20日
    浏览(29)
  • Spring Boot学习随笔- 实现AOP(JoinPoint、ProceedingJoinPoint、自定义注解类实现切面)

    学习视频:【编程不良人】2021年SpringBoot最新最全教程 问题 现有业务层开发存在问题 额外功能代码存在大量冗余 每个方法都需要书写一遍额外功能代码不利于项目维护 Spring中的AOP AOP:Aspect 切面 + Oriented 面向 Programmaing 面向切面编程 Aspect(切面) = Advice(通知) + Pointcut(

    2024年02月04日
    浏览(33)
  • 三方开放接口,Springboot通过AOP实现API接口的签名验证

    前言 对外开放的接口,需要验证请求方发送过来的数据确实是由发送方发起的,并且中途不能被篡改和伪造,所以才会对接口的访问进行签名验证,以保证双方获取到的原来的信息是没有经过篡改的。 实现方法 对请求的信息内容,通过MD5运算或者其他算法(必须是 不可逆的

    2024年02月07日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包