两种通过aop设置重试机制的方式

2023-11-13

注意:

1.不要在同一个类中调用自定义的注解,如果controller调用,注解要放在service层(其他类)

2.如果有配置aop扫描的包,不能只设置扫描control下的文件

 

方式一:

/**
* controller层
*/
@RequestMapping(value = "/feedback/test", method = RequestMethod.POST)
    public void test() throws Exception{

        /**
         * 不能在本类中调用带注解的方法
         *
         * 先进入方法再进入切面
         */
        demoService.genBigNum();
    }
/**
* service层
*/
@Component
public class DemoService {

    @Retry(count = 5, sleep = 2 * 1000)
    public int genBigNum() throws Exception {
        int a = (int) (Math.random() * 10);
        System.out.println("genBigNum " + a);
        if (a < 300) {
            throw new Exception("num less than 300");
        }

        return a;
    }
}
/**
 * 自定义注解
 *
 * @author leon
 * @date 2019/08/19
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {
    /**
     * 重试次数
     * @return
     */
    int count() default 0;


    /**
     * 重试的间隔时间
     * @return
     */
    int sleep() default 0;


    /**
     * 是否支持异步重试方式
     * @return
     */
    boolean asyn() default false;
}
/**
 * 模版
 *
 * @author leon
 * @date 2019/08/19
 */
public abstract class RetryTemplate {

    private static final int DEFAULT_RETRY_TIME = 1;

    private int retryTime = DEFAULT_RETRY_TIME;

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryTemplate.class);

    // 重试的睡眠时间
    private int sleepTime = 0;

    public int getSleepTime() {
        return sleepTime;
    }

    public RetryTemplate setSleepTime(int sleepTime) {
        if(sleepTime < 0) {
            throw new IllegalArgumentException("sleepTime should equal or bigger than 0");
        }

        this.sleepTime = sleepTime;
        return this;
    }

    public int getRetryTime() {
        return retryTime;
    }

    public RetryTemplate setRetryTime(int retryTime) {
        if (retryTime <= 0) {
            throw new IllegalArgumentException("retryTime should bigger than 0");
        }

        this.retryTime = retryTime;
        return this;
    }

    /**
     * 留给业务方实现
     * 重试的业务执行代码
     * 失败时请抛出一个异常
     *
     * todo 确定返回的封装类,根据返回结果的状态来判定是否需要重试
     *
     * @return
     */
    protected abstract Object doBiz() throws Throwable;


    public Object execute() throws InterruptedException {
        for (int i = 0; i < retryTime; i++) {
            try {
                return doBiz();
            } catch (Throwable e) {
                LOGGER.error("业务执行出现异常,e: {}", e);
                Thread.sleep(sleepTime);
            }
        }

        return null;
    }


    public Object submit(ExecutorService executorService) {
        if (executorService == null) {
            throw new IllegalArgumentException("please choose executorService!");
        }

        return executorService.submit((Callable) () -> execute());
    }

}
/**
 * 切面
 *
 * @author leon
 * @date 2019/08/19
 */
@Aspect
@Component
@Slf4j
public class RetryAspect {

    /**
     * 注意:此处使用了无界队列(LinkedBlockingQueue)
     */
    ExecutorService executorService = new ThreadPoolExecutor(3, 5,
        1, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());

    @Around(value = "@annotation(retry)")
    public Object execute(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
        RetryTemplate retryTemplate = new RetryTemplate() {
            @Override
            protected Object doBiz() throws Throwable{
                return joinPoint.proceed();
            }
        };

        retryTemplate.setRetryTime(retry.count())
            .setSleepTime((int)retry.sleep());


        if (retry.asyn()) {
            return retryTemplate.submit(executorService);
        } else {
            return retryTemplate.execute();
        }
    }
}

 

 

方法2:

    /**
     * controller层
     *
     * 直接进入切面,再返回到方法
     * @throws Exception
     */
    @RequestMapping(value = "/feedback/test1", method = RequestMethod.POST)
    @RetryDot(times = 5, waitTime = 2 * 1000)
    public void aa() throws Exception{

       String ss = null;
       ss.length();
    }
/**
 * 自定义注解
 * <用来异常重试>
 * 注意:needThrowExceptions & catchExceptions 是相关联的, 有顺序依赖。
 * 当这两个数组的长度都为0时, 直接执行重试逻辑。
 *
 * @author zmc
 * @date 2019/08/19
 * @see ExceptionRetryAspect
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryDot {
    /**
     * 设置失败之后重试次数,默认为1次。
     * 少于1次,则默认为1次
     * 推荐最好不要超过5次, 上限为10次
     * 当没有重试次数时, 会将异常重新抛出用来定位问题。
     *
     * @return
     */
    int times() default 1;

    /**
     * 重试等待时间,时间单位为毫秒。默认是 0.5 * 1000ms, 小于等于0则不生效
     * 推荐不要超过 3 * 1000ms
     * 上限为 10 * 1000ms
     *
     * @return
     */
    long waitTime() default 500;

    /**
     * 需要抛出的异常, 这些异常发生时, 将直接报错, 不再重试。
     * 传入一些异常的class对象
     * 如UserException.class
     * 当数组长度为0时, 那么都不会抛出, 会继续重试
     *
     * @return 异常数组
     */
    Class[] needThrowExceptions() default {};

    /**
     * 需要捕获的异常, 如果需要捕获则捕获重试。否则抛出异常
     * 执行顺序 needThrowExceptions --> catchExceptions 两者并不兼容
     * 当 needThrowExceptions 判断需要抛出异常时, 抛出异常, 否则进入此方法, 异常不在此数组内则抛出异常
     * 当数组长度为0时, 不会执行捕获异常的逻辑。
     *
     * @return 异常数组
     */
    Class[] catchExceptions() default {};


    /**
     * 是否支持异步重试方式
     * @return
     */
    boolean asyn() default false;
/**
 * 切面
 * controller(调用之后)会直接进入该切面;
 * 跑到joinPoint.proceed()才回到业务层
 * 捕获异常之后再回到该切面进行循环重试
 *
 * <异常重试切面>
 *
 * @author leon
 * @date 2019/08/19
 */
@Aspect
@Component
public class ExceptionRetryAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionRetryAspect.class);

    @Pointcut("@annotation(com.fangdd.vr.ip.fdd.server.annotate.RetryDot)")
    public void retryPointCut() {
    }

    @Around("retryPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        LOGGER.info("进入切面...");
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        RetryDot retry = method.getAnnotation(RetryDot.class);
        String name = method.getName();
        Object[] args = joinPoint.getArgs();
        String uuid = UUID.randomUUID().toString();
        LOGGER.info("执行重试切面{}, 方法名称{}, 方法参数{}", uuid, name, JSON.toJSONString(args));
        int times = retry.times();
        long waitTime = retry.waitTime();
        Class[] needThrowExceptions = retry.needThrowExceptions();
        Class[] catchExceptions = retry.catchExceptions();
        // check param
        if (times <= 0) {
            times = 1;
        }

        for (; times >= 0; times--) {
            try {
                return joinPoint.proceed();
            } catch (Exception e) {
                // 如果需要抛出的异常不是空的, 看看是否需要抛出
                if (needThrowExceptions.length > 0) {
                    for (Class exception : needThrowExceptions) {
                        if (exception == e.getClass()) {
                            LOGGER.warn("执行重试切面{}失败, 异常在需要抛出的范围{}, 业务抛出的异常类型{}", uuid, needThrowExceptions,
                                e.getClass().getName());
                            throw e;
                        }
                    }
                }

                // 如果需要抛出异常,而且需要捕获的异常为空那就需要再抛出
                if (catchExceptions.length > 0) {
                    boolean needCatch = false;
                    for (Class catchException : catchExceptions) {
                        if (e.getClass() == catchException) {
                            needCatch = true;
                            break;
                        }
                    }
                    if (!needCatch) {
                        LOGGER.warn("执行重试切面{}失败, 异常不在需要捕获的范围内, 需要捕获的异常{}, 业务抛出的异常类型{}", uuid, catchExceptions,
                            e.getClass().getName());
                        throw e;
                    }
                }

                // 如果接下来没有重试机会的话,直接报错
                if (times <= 0) {
                    LOGGER.warn("执行重试切面{}失败", uuid);
                    throw e;
                }

                // 休眠 等待下次执行
                if (waitTime > 0) {
                    Thread.sleep(waitTime);
                }

                LOGGER.warn("执行重试切面{}, 还有{}次重试机会, 异常类型{}, 异常信息{}, 栈信息{}", uuid, times, e.getClass().getName(), e.getMessage(), e.getStackTrace());
            }
        }
        return false;
    }
}

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两种通过aop设置重试机制的方式 的相关文章

  • 为什么编译后代码注入比预编译代码注入更好?

    所以我们都知道 C 没有类似 C 的宏预处理器 并且有一个很好的线程解释了为什么here https stackoverflow com questions 1369725 why arent there macros in c 但现在 A
  • 非 Spring 组件的 Spring AOP

    我正在使用 java 配置编写 Spring 4 应用程序 我可以在这个项目中对所有 spring 组件使用 AOP 但我不能将它用于普通的 POJO 类 我需要添加什么库以及我需要在配置文件中放入什么配置才能使其正常工作 Spring A
  • 如何使用 Spring AOP 在类上创建一个方面,而不是一个 bean?

    我在一个遗留应用程序上工作 其中 Spring AOP 即ProxyFactoryBean 用来 我需要围绕某个类的方法添加一个方面 这堂课是not然而一颗豆子 AspecjJ 切入点表达式如下所示 execution xyz packag
  • 如何在 Spring 配置文件中混合使用 CGLIB 和 JDK 代理?

    该线程与我遇到的一个问题有关这里关于访问建议类的受保护方法的需求 https stackoverflow com q 10110253 827480 我正在使用 Spring 3 0 6 并创建了一个 Spring 分析方面 我将其应用于使
  • 为反应式管道编写方面

    我正在为返回承诺的方法编写方面 考虑以下方法 public Mono
  • AOP 围绕外部库的重写方法?

    我正在寻找以下问题的实用解决方案 外部库提供组件作为基类 自定义组件是通过扩展这些基类来创建的 当实现抛出未处理的异常时 基类就会中断 基类源代码不可用 只有一个二进制罐子 我正在寻找的是一个通用的 AOP 错误处理建议 它将包装每个方法的
  • 各种切入点表达式作用域意外触发多个通知调用

    背景 使用方面记录项目 例如所有标有 Log注释将信息写入日志文件 Problem 方法似乎是递归调用一级深度的 但代码没有显示任何这样的递归关系 Actual 记录结果 2018 09 25 12 17 29 155 EmailNotif
  • Spring AOP 创建额外的 bean

    我正在玩Spring AOP 这是一个简单的类 public class CModel extends Car private double torqueMeasure 1 public CModel System out println
  • Spring AOP 切入点,方法名以 get 开头

    我正在尝试为 Spring AOP 实现切入点 所有的方法都像getXXXX应该被记录 我尝试了以下方法 但要么抛出异常 要么不触发 1st try Pointcut within net services get private void
  • Spring AOP Aspect 无法使用 Mockito 工作

    我有一个 Aspect它编织了我所有控制器操作方法的执行 当我运行系统时它工作得很好 但在单元测试中却不行 我按以下方式使用 Mockito 和 junit RunWith SpringJUnit4ClassRunner class Con
  • Spring Boot 记录器方面

    当访问特定包的类中的方法时 我在日志方面记录信息时遇到问题 换句话说 不 记录发生 我什至绝望地添加了 System out println 语句 但没有成功 我所有的课程都位于org my package包 即org my package
  • 如何将AOP引入到生产性软件开发中?

    我知道这个问题以前曾被问过 但这是一年半前的事了 尽管我认为现在可能是重新提问的时候了 我也认识到它可能被视为主观的 但我认为支持 反对 AOP 是有客观原因的 我有兴趣谁在使用AOP在软件开发中也why or why not使用它 我认为
  • Spring 3.1实例化bean时出错

    我有以下课程 public abstract class AbstractBusinessModule public class MS3BusinessModule extends AbstractBusinessModule public
  • 调用 around 方面时出现 AOP 异常

    我正在尝试在所有服务方法上运行一个方面 但这对于具有原始返回类型的方法似乎失败 我收到此错误 org springframework aop AopIn VocationException Null return value from su
  • 从 joinPoint 获取 HTTP 方法

    我需要从某个方面的 joinPoint 获取像 POST PATCH GET etc 这样的 http 方法 Before isRestController public void handlePost JoinPoint point do
  • 如何在 Spring 中管理对象池?

    据我了解 在 Spring 中 所有对象默认都被视为单例 如果 singleton 设置为 false 则每个请求都会提供一个新对象 但是如果我想池化对象怎么办 假设设置范围从最少 1 到最多 10 个实例 使用 Spring 可以吗 池化
  • Android studio 的aspectj - AJDT 插件在哪里

    如何为 android studio 安装 AJDT 我知道 Eclipse 有一个 但我在 android studio 中找不到任何东西 我想做的是在启动之前对我的每个方法调用进行安全检查 我已经从这个网站尝试过AJDT http ec
  • 使用 PostSharp 添加 OnException 属性

    我正在尝试一些 AOP 似乎 NET PostSharp 是可行的方法 我想在发生异常时对数据库进行一些简单的日志记录 然而 我发现除了基础知识之外 很难找到任何使用 PostSharp 的真实可靠的例子 我尝试了以下方法 Serializ
  • Ninject 通过城堡动态代理拦截具有非空构造函数的代理类

    我当前的大部分实现都基于此处提供的信息 Ninject 拦截任何具有特定属性的方法吗 https stackoverflow com questions 6386461 ninject intercept any method with c
  • HK2 MethodInterceptor 与 Jersey 资源

    如何设置aopMethodInterceptor使用泽西岛资源 这是我尝试过的 如下this https hk2 java net 2 2 0 aop example html文档 第 1 步 拦截服务 public class MyInt

随机推荐

  • c语言文件处理中ab,C语言文件处理中wt是什么操作方式?

    匿名用户 1级 2013 04 25 回答 最常用的文件使用方式及其含义如下 1 r 为读而打开文本文件 不存在则出错 2 rb 为读而打开二进制文件 3 w 为写而打开文本文件 若不存在则新建 反之 则从文件起始位置写 原内容将被覆盖 4
  • 【中间件】Redis如何解决BigKey

    BigKey 的弊端 BigKey 需要解决 根源就在于 BigKey 会带来的问题 占用内存 因为 Redis 数据结构的底层数据结构 大 Key 会占用更多的内存空间 造成更大的内存消耗 单线程模型 因为 Redis 的通信依赖于 So
  • 一文看懂web服务器、应用服务器、web容器、反向代理服务器区别与联系

    我们知道 不同肤色的人外貌差别很大 而双胞胎的辨识很难 有意思的是Web服务器 Web容器 Web应用程序服务器 反向代理有点像四胞胎 在网络上经常一起出现 本文将带读者对这四个相似概念如何区分 Web服务器概念与基本原理 Web服务器的历
  • CSS基础之CSS文本属性

    文章目录 前言 1 color 2 text align 3 font size 4 text decoration 5 text indent 6 line height 7 文本属性总结 前言 CSS 文本属性可以设置文本的 外观 比如
  • 从同源政策到跨域解决方法

    一 同源政策 同源政策的目的 是为了保证用户信息的安全 防止恶意的网站窃取数据 所谓同源指的是协议 域名 端口相同 否则就会产生跨域问题 二 跨域 跨域问题主要分为三类 1 Cookie LocalStorage 和 IndexDB 无法读
  • 记一次jQuery EasyUI使用-Easyui combobox的使用方法

    开局附上最最最有用的官方文档 划重点 easyui使用手册 进入正题 现象 有这样一段代码 浏览器请求getSystemSignList方法有返回数据并且严格符合easyui的应答规范 一个json格式的list对象 tr td class
  • 大模型讲习班丨第四范式黄世宇:强化学习的发展历程与基于人类反馈的强化学习...

    人工智能研究与应用范式正经历一场剧变 越来越多的顶级团队和杰出人才纷纷加入这一变革浪潮 作为AI大模型科研先锋 智源研究院携手一批卓越的学者与工程师 致力于将尖端技术与经验传授给有潜力的学习者 通过高效的学习方式 让更多人能迅速融入这一重要
  • MobileNet网络结构详解

    下图展示了传统卷积与DW卷积的差异 在传统卷积中 每个卷积核的channel与输入特征矩阵的channel相等 每个卷积核都会与输入特征矩阵的每一个维度进行卷积运算 而在DW卷积中 每个卷积核的channel都是等于1的 每个卷积核只负责输
  • Python-安装库-图像处理库-cv2

    问题 在pycharm中搜索cv2库 发现没有版本 在网上查找资料 找到了类似官方文档的资料 提到了安装方法 https pypi org project opencv python description cv2介绍 CV2指的是Open
  • ERROR 1064 (42000): You have an error in your SQL syntax

    mysql使用load data infile导入数据 出现如下错误 root NoName 21 19 12 gt load data infile change csv into table change CHARACTER SET u
  • JAVA基础知识点大全之二

    1 泛型 1 1 泛型类 定义格式 修饰符 class 类名 lt 类型 gt 1 2 泛型方法 定义格式 修饰符 lt 类型 gt 返回值类型 方法名 类型 变量名 1 3 泛型接口 定义格式 修饰符 interface 接口名 lt 类
  • c/c++多线程编程(1):线程的创建

    参考资料 多线程和线程同步 C C 运行环境 wsl2 Ubuntu 20 04 vscode clangd xmake gcc9 4 0 1 创建线程 1 1 线程函数 每个线程都有一个属于自己的线程id id的类型为phtread t
  • 解决centos 8命令ip add无效问题

    之前用Xshell连接虚拟机一直正常 突然一台节点总是连不上 查询众多资料后 终于找到了问题所在 出错情况 输入命令 root node01 service NetworkManager start root node01 nmcli ne
  • 图腾柱电路工作原理

    图腾柱就是上下各一个晶体管 上管为NPN c极接正电源 下管为PNP e极接负电源 注意 是负电源 是地 两个b极接到一起 接输入 上管的e和下管的c接到一起 接输出 用来匹配电压 或者提高IO口的驱动能力 有几种图腾柱电路的变种 一种是两
  • Log4j2安全 JNDI漏洞 CVE-2021-44228

    Apache Log4j2是基于Java的日志记录工具 工具重写了Log4j框架 并且引入了大量丰富特性 该日志框架被大量用于业务系统开发 用来记录日志信息 大多数情况下 开发者可能会将用户输入导致的错误信息写入日志中 因该组件使用极为广泛
  • linux内核态发送tcp包,Linux内核发送构造数据包的方式

    本文欢迎自由转载 但请标明出处 并保证本文的完整性 作者 Godbach 日期 2009 09 01 一 构造数据包简析 这里并不详细介绍如何在内核中构造数据包 下文如有需要会在适当的位置进行分析 这里简单的分析讲一下内核态基于Netfil
  • 系统掌握数据结构8 树与二叉树 第二节

    树与二叉树 2节 1 线索二叉树的逻辑结构 2 线索二叉树的物理结构 3 中序线索二叉树 3 1 逻辑结构 3 2 代码实现 4 先序线索二叉树 5 后序线索二叉树 6 三叉链表的物理结构 7 先序线索二叉树的三叉链表存储实现 8 后序线索
  • 【转】C#操作sqlServer数据库

    转载地址 https blog csdn net weixin 42731241 article details 81172622 工具 vs2015 SqlServer 数据库的连接及打开关闭 VS2015建立一个c 的控制台应用程序 必
  • 10个 解放双手的 IDEA 插件,让你少写冤枉代码

    公众号关注 GitHubDaily 设为 星标 每天带你逛 GitHub 友情提示 插件虽好 可不要贪装哦 装多了会 卡 卡 卡 正经干活用的 分享一点自己工作中得心应手的 IDEA 插件 可不是在插件商店随随便便搜的 都经过实战检验 用过
  • 两种通过aop设置重试机制的方式

    注意 1 不要在同一个类中调用自定义的注解 如果controller调用 注解要放在service层 其他类 2 如果有配置aop扫描的包 不能只设置扫描control下的文件 方式一 controller层 RequestMapping