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参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • nas实现java开发的环境详解

    nas实现java开发的环境详解

    这篇文章主要为大家介绍了nas实现java开发的环境详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • spring整合struts2过程详解

    spring整合struts2过程详解

    这篇文章主要介绍了spring整合struts2过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java 利用DeferredResult实现http轮询实时返回数据接口

    Java 利用DeferredResult实现http轮询实时返回数据接口

    这篇文章主要介绍了Java 利用 DeferredResult 实现 http 轮询实时返回数据接口,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-03-03
  • RestTemplate的DELETE及PUT等请求方法使用精讲

    RestTemplate的DELETE及PUT等请求方法使用精讲

    这篇文章主要为大家介绍了RestTemplate的DELETE及PUT等请求方法的使用精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-03-03
  • Java多线程中的wait/notify通信模式实例详解

    Java多线程中的wait/notify通信模式实例详解

    这篇文章主要给大家介绍了关于Java多线程中wait/notify通信模式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Java 反射机制

    Java 反射机制

    这篇文章简要的说明了Java的反射机制,Java的反射是框架设计的灵魂,本文通过例子能看的更加清晰的理解
    2021-06-06
  • springboot下mybatis-plus开启打印sql日志的配置指南

    springboot下mybatis-plus开启打印sql日志的配置指南

    这篇文章主要给大家介绍了关于springboot下mybatis-plus开启打印sql日志的配置指南的相关资料,还介绍了关闭打印的方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • 详解Java的MyBatis框架中的缓存与缓存的使用改进

    详解Java的MyBatis框架中的缓存与缓存的使用改进

    很多人在使用MyBatis的缓存后经常会遇到MySQL分页查询的显示问题,针对于此,这里我们就来详解Java的MyBatis框架中的缓存与缓存的使用改进,首先来回顾一下MyBatis的缓存机制与执行:
    2016-06-06
  • Java C++分别实现滑动窗口的最大值

    Java C++分别实现滑动窗口的最大值

    这篇文章主要介绍了分别通过Java和C++实现滑动窗口最大值,即给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。感兴趣的可以了解一下
    2021-12-12
  • java邮件发送的实现

    java邮件发送的实现

    本篇是用于java来实现邮件的发送的实例代码,基于smtp服务,有需要的可以参考一下。
    2016-10-10

最新评论