Spring中的接口重试机制spring-retry之listeners参数解析

 更新时间:2024年01月24日 08:50:22   作者:北漂码农有话说  
这篇文章主要介绍了Spring中的接口重试机制spring-retry之listeners参数解析,注解@Retryable有一个参数listeners没有说明,那么本篇文章我们详细介绍一个这个参数的用,需要的朋友可以参考下

背景

上篇文章中我们简单介绍了spring-retry的功能及简单用法,但是注解@Retryable还有一个参数listeners我们没有进行说明, 那么本篇文章我们详细介绍一个这个参数的用法。

分析

由参数名字我们我们可以知道,这里面可以配置一些监听器。

那这些监听器该如何进行配置呢?首先我们分析源码。

注解源码

我们只保留这个参数的源码,其他的省略掉了。

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
	/**
	 * Bean names of retry listeners to use instead of default ones defined in Spring
	 * context
	 * @return retry listeners bean names
	 */
	String[] listeners() default {};
}

说明:源码中该参数的解释:使用的重试监听器的Bean名称,而不是在Spring上下文中定义的默认名称,我们可以大胆的猜测出 这个是一个特定的Bean,需要开发者自己定义。

并且可以接收的参数是数组形式,那么问题是如何定义呢?

处理注解源码

我们上篇文章中提到,处理注解 @Retryable的类为:AnnotationAwareRetryOperationsInterceptor,那么我们就在这个类中找寻我们要的答案。

方法getListenersBeans() 源码

private RetryListener[] getListenersBeans(String[] listenersBeanNames) {
    RetryListener[] listeners = new RetryListener[listenersBeanNames.length];
    for (int i = 0; i < listeners.length; i++) {
        listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class);
    }
    return listeners;
}

有上面的代码我们可以知道,我们自己定义的监听器肯定和RetryListener有着某种关系。下面我们分析该类的源码

RetryListener 源码

/**
 * Interface for listener that can be used to add behaviour to a retry. Implementations of
 * {@link RetryOperations} can chose to issue callbacks to an interceptor during the retry
 * lifecycle.
 */
public interface RetryListener {
	/**
	 * Called before the first attempt in a retry. For instance, implementers can set up
	 * state that is needed by the policies in the {@link RetryOperations}. The whole
	 * retry can be vetoed by returning false from this method, in which case a
	 * {@link TerminatedRetryException} will be thrown.
	 * @param <T> the type of object returned by the callback
	 * @param <E> the type of exception it declares may be thrown
	 * @param context the current {@link RetryContext}.
	 * @param callback the current {@link RetryCallback}.
	 * @return true if the retry should proceed.
	 */
	<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
	/**
	 * Called after the final attempt (successful or not). Allow the interceptor to clean
	 * up any resource it is holding before control returns to the retry caller.
	 * @param context the current {@link RetryContext}.
	 * @param callback the current {@link RetryCallback}.
	 * @param throwable the last exception that was thrown by the callback.
	 * @param <E> the exception type
	 * @param <T> the return value
	 */
	<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
	/**
	 * Called after every unsuccessful attempt at a retry.
	 * @param context the current {@link RetryContext}.
	 * @param callback the current {@link RetryCallback}.
	 * @param throwable the last exception that was thrown by the callback.
	 * @param <T> the return value
	 * @param <E> the exception to throw
	 */
	<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
}

分析:首先这个是一个接口,那不用想了,开发者自定义的监听器,必然要实现这个接口。

实现

由于listeners这个参数可以是多个,并且入参是一个数组,我们先来定义两哥监听器。代码如下

RetryListenerBean

@Slf4j
public class RetryListenerBean implements RetryListener {

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("执行了 open 方法 ");
        return true;
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("执行了 onError 方法,说明出现了异常");
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("执行了 close 方法 ");
    }
}

RetryListenerTwoBean

@Slf4j
public class RetryListenerTwoBean implements RetryListener {

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("执行了 第二个  open 方法 ");
        return true;
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("执行了 第二个 onError 方法,说明出现了异常");
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("执行了 第二个 close 方法 ");
    }
}

分析1:我们定义了自己的两个监听器,在参数listeners进行配置,代码如下:

@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5),listeners = {"retryListenerBean", "retryListenerTwoBean"} )

分析2:那么框架是如何识别到我们配置的监听器呢?请看源码

for (int i = 0; i < listeners.length; i++) {
    listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class);
}

分析3:在上面的代码中,我们发现了beanFactory#getBean(),那么一切真相大白。因此我们必须将自己定义的监听器 交由Spring 进行管理。所以我们需要将自己定义的监听器进行配置。

监听器配置

我们将自己定义的监听器进行配置,由Spring进行管理,配置代码如下:

@Configuration
public class Config {

    @Bean("retryListenerBean")
    public RetryListenerBean listenerBean(){
        return new RetryListenerBean();
    }

    @Bean("retryListenerTwoBean")
    public RetryListenerTwoBean listenerTwoBean(){
        return new RetryListenerTwoBean();
    }

}

进行如上代码配置后,就可以通过Bean的名称通过getBean的方法进行获取监听器的实例了。

测试

启动项目,我们使用postman进行测试。

日志如下:

2023-07-23 14:37:07.102  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.controller.RetryController       : Controller 请求入参为:222
2023-07-23 14:37:25.312  INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean   : 执行了 open 方法 
2023-07-23 14:37:25.313  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean        : 执行了 第二个  open 方法 
2023-07-23 14:37:30.816  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:222
2023-07-23 14:37:31.285  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Sun Jul 23 14:37:31 CST 2023
2023-07-23 14:37:38.312  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean        : 执行了 第二个 onError 方法,说明出现了异常
2023-07-23 14:37:38.313  INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean   : 执行了 onError 方法,说明出现了异常
2023-07-23 14:37:54.729  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:222
2023-07-23 14:37:54.729  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Sun Jul 23 14:37:54 CST 2023
2023-07-23 14:37:54.729  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean        : 执行了 第二个 onError 方法,说明出现了异常
2023-07-23 14:37:54.729  INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean   : 执行了 onError 方法,说明出现了异常
2023-07-23 14:38:02.498  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:222
2023-07-23 14:38:02.499  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Sun Jul 23 14:38:02 CST 2023
2023-07-23 14:38:02.499  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean        : 执行了 第二个 onError 方法,说明出现了异常
2023-07-23 14:38:02.499  INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean   : 执行了 onError 方法,说明出现了异常
2023-07-23 14:38:02.500  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl    : 异常出现后的回调操作,入参为:222,当前时间为:Sun Jul 23 14:38:02 CST 2023
2023-07-23 14:38:02.501  INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean        : 执行了 第二个 close 方法 
2023-07-23 14:38:02.502  INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean   : 执行了 close 方法 

以上的日志,可以看出来我们定义的监听器中的日志输出,监听器的逻辑进行了执行。

小结

由监听器的代码,其中包括三个方案open、onError和close我们结合日志的输出顺序。

首先在执行我们的业务逻辑之前,先执行 open方案,相当于一个前置拦截器,我们可以在这个方法中实现一些前置的逻辑操作。

遇到异常的情况会执行onError方法。最终会 执行close方法。因此我们在不同的阶段利用这三个方法可以实现我们的想要的业务逻辑。

到此这篇关于Spring中的接口重试机制spring-retry之listeners参数解析的文章就介绍到这了,更多相关spring-retry之listeners参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java Reactor反应器模式使用方法详解

    Java Reactor反应器模式使用方法详解

    这篇文章主要介绍了Java Reactor反应器模式使用方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • 使用RestTemplate访问https实现SSL请求操作

    使用RestTemplate访问https实现SSL请求操作

    这篇文章主要介绍了使用RestTemplate访问https实现SSL请求操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • java中redis增删查以及清理缓存的案例

    java中redis增删查以及清理缓存的案例

    这篇文章主要介绍了java中redis增删查以及清理缓存的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • 详解SpringCloud微服务架构之Hystrix断路器

    详解SpringCloud微服务架构之Hystrix断路器

    本篇文章主要介绍了详解SpringCloud微服务架构之Hystrix断路器,Hystrix是一个库,通过添加延迟容差和容错逻辑来帮助您控制这些分布式服务之间的交互,有兴趣的可以了解一下
    2018-01-01
  • Java中synchronized 的4个优化技巧

    Java中synchronized 的4个优化技巧

    本文主要介绍了Java中synchronized的4个优化技巧,synchronized在JDK 1.5 时性能是比较低的,然而在后续的版本中经过各种优化迭代,它的性能也得到了前所未有的提升,下文更多相关资料需要的小伙伴可以参考一下
    2022-05-05
  • java 多线程饥饿现象的问题解决方法

    java 多线程饥饿现象的问题解决方法

    这篇文章主要介绍了java 多线程饥饿现象的问题解决方法的相关资料,需要的朋友可以参考下
    2017-06-06
  • Java语法基础之函数的使用说明

    Java语法基础之函数的使用说明

    函数就是定义在类中的具有特定功能的一段小程序,函数也称为方法
    2013-07-07
  • Java中字符串和byte数组之间的简单转换方法

    Java中字符串和byte数组之间的简单转换方法

    这篇文章主要给大家介绍了关于Java中字符串和byte数组之间的简单转换方法,Java中将String类型转换为byte[]类型,可以使用String的getBytes()方法,还有很多其他的办法,需要的朋友可以参考下
    2023-08-08
  • 解决springboot 实体类String转Date类型的坑

    解决springboot 实体类String转Date类型的坑

    这篇文章主要介绍了解决springboot 实体类String转Date类型的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 聊聊单线程的Redis为何会快到飞起

    聊聊单线程的Redis为何会快到飞起

    Redis想必大家都或多或少听过吧,我们在工作学习中通常用它来作为缓存使用,既然是作为缓存,大家的第一反应肯定是:这家伙很快
    2022-02-02

最新评论