1、概述

Spring Retry 是Spring框架中的一个组件,

它提供了自动重新调用失败操作的能力。这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助。

在本文中,我们将看到使用Spring Retry的各种方式:注解、RetryTemplate以及回调。

2、Maven依赖

让我们首先将spring-retry依赖项添加到我们的pom.xml文件中:

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>

我们还需要将Spring AOP添加到我们的项目中:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>

可以查看Maven Central来获取最新版本的spring-retry

spring-aspects 依赖项。

3、开启Spring Retry

要在应用程序中启用Spring Retry,我们需要将@EnableRetry注释添加到我们的@Configuration类:

@Configuration
@EnableRetry
public class AppConfig { ... }

4、使用Spring Retry

4.1、@Retryable而不用恢复

我们可以使用@Retryable注解为方法添加重试功能:

@Service
public interface MyService {
@Retryable(value = RuntimeException.class)
void retryService(String sql); }

在这里,当抛出RuntimeException时尝试重试。

根据@Retryable的默认行为,重试最多可能发生3次,重试之间有1秒的延迟。

4.2、@Retryable@Recover

现在让我们使用@Recover注解添加一个恢复方法:

@Service
public interface MyService {
@Retryable(value = SQLException.class)
void retryServiceWithRecovery(String sql) throws SQLException; @Recover
void recover(SQLException e, String sql);
}

这里,当抛出SQLException时重试会尝试运行。 当@Retryable方法因指定异常而失败时,@Recover注解定义了一个单独的恢复方法。

因此,如果retryServiceWithRecovery方法在三次尝试之后还是抛出了SQLException,那么recover()方法将被调用。

恢复处理程序的第一个参数应该是Throwable类型(可选)和相同的返回类型。其余的参数按相同顺序从失败方法的参数列表中填充。

4.3、自定义@Retryable的行为

为了自定义重试的行为,我们可以使用参数maxAttemptsbackoff

@Service
public interface MyService {
@Retryable( value = SQLException.class,
maxAttempts = 2, backoff = @Backoff(delay = 100))
void retryServiceWithCustomization(String sql) throws SQLException;
}

这样最多将有两次尝试和100毫秒的延迟。

4.4、使用Spring Properties

我们还可以在@Retryable注解中使用properties。

为了演示这一点,我们将看到如何将delaymaxAttempts的值外部化到一个properties文件中。

首先,让我们在名为retryConfig.properties的文件中定义属性:

retry.maxAttempts=2
retry.maxDelay=100

然后我们指示@Configuration类加载这个文件:

@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }
// ...

最后,我们可以在@Retryable的定义中注入retry.maxAttemptsretry.maxDelay的值:

@Service
public interface MyService {
@Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
void retryServiceWithExternalizedConfiguration(String sql) throws SQLException;
}

请注意,我们现在使用的是maxAttemptsExpressiondelayExpression而不是maxAttemptsdelay

5、RetryTemplate

5.1、RetryOperations

Spring Retry提供了RetryOperations接口,它提供了一组execute()方法:

public interface RetryOperations {
<T> T execute(RetryCallback<T> retryCallback) throws Exception; ...
}

execute()方法的参数RetryCallback,是一个接口,可以插入需要在失败时重试的业务逻辑:

public interface RetryCallback<T> {
T doWithRetry(RetryContext context) throws Throwable;
}

5.2、RetryTemplate配置

RetryTemplateRetryOperations的一个实现。

让我们在@Configuration类中配置一个RetryTemplate的bean:

@Configuration
public class AppConfig {
//...
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate(); FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(2000l);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy); SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(2);
retryTemplate.setRetryPolicy(retryPolicy); return retryTemplate;
}
}

这个RetryPolicy确定了何时应该重试操作。

其中SimpleRetryPolicy定义了重试的固定次数,另一方面,BackOffPolicy用于控制重试尝试之间的回退。

最后,FixedBackOffPolicy会使重试在继续之前暂停一段固定的时间。

5.3、使用RetryTemplate

要使用重试处理来运行代码,我们可以调用retryTemplate.execute()方法:

retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
@Override
public Void doWithRetry(RetryContext arg0) {
myService.templateRetryService();
...
}
});

我们可以使用lambda表达式代替匿名类:

retryTemplate.execute(arg0 -> {
myService.templateRetryService();
return null;
});

6、监听器

监听器在重试时提供另外的回调。我们可以用这些来关注跨不同重试的各个横切点。

6.1、添加回调

回调在RetryListener接口中提供:

public class DefaultListenerSupport extends RetryListenerSupport {
@Override
public <T, E extends Throwable> void close(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
logger.info("onClose");
...
super.close(context, callback, throwable);
} @Override
public <T, E extends Throwable> void onError(RetryContext context,
RetryCallback<T, E> callback, Throwable throwable) {
logger.info("onError");
...
super.onError(context, callback, throwable);
} @Override
public <T, E extends Throwable> boolean open(RetryContext context,
RetryCallback<T, E> callback) {
logger.info("onOpen");
...
return super.open(context, callback);
}
}

openclose的回调在整个重试之前和之后执行,而onError应用于单个RetryCallback调用。

6.2、注册监听器

接下来,我们将我们的监听器(DefaultListenerSupport)注册到我们的RetryTemplate bean:

@Configuration
public class AppConfig {
... @Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
...
retryTemplate.registerListener(new DefaultListenerSupport());
return retryTemplate;
}
}

7、测试结果

为了完成我们的示例,让我们验证一下结果:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
classes = AppConfig.class,
loader = AnnotationConfigContextLoader.class)
public class SpringRetryIntegrationTest { @Autowired
private MyService myService; @Autowired
private RetryTemplate retryTemplate; @Test(expected = RuntimeException.class)
public void givenTemplateRetryService_whenCallWithException_thenRetry() {
retryTemplate.execute(arg0 -> {
myService.templateRetryService();
return null;
});
}
}

从测试日志中可以看出,我们已经正确配置了RetryTemplateRetryListener

2020-01-09 20:04:10 [main] INFO  c.p.s.DefaultListenerSupport - onOpen
2020-01-09 20:04:10 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onError
2020-01-09 20:04:12 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onError
2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onClose

8、结论

在本文中,我们看到了如何使用注解、RetryTemplate和回调监听器来使用Spring Retry。

原文地址:https://www.baeldung.com/spring-retry

翻译:码农熊猫

更多技术干货,请访问我的个人网站https://pinmost.com,或关注公众号【码农熊猫】

Spring框架中一个有用的小组件:Spring Retry的更多相关文章

  1. spring框架中一个跟String的trim方法一样的方法

    @Test public void testTrimWhitespace() throws Exception { assertEquals(null, StringUtils.trimWhitesp ...

  2. 细说shiro之五:在spring框架中集成shiro

    官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...

  3. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  4. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  5. 漫谈 GOF 设计模式在 Spring 框架中的实现

    原文地址:梁桂钊的博客 博客地址:http://blog.720ui.com 欢迎关注公众号:「服务端思维」.一群同频者,一起成长,一起精进,打破认知的局限性. 漫谈 GOF 设计模式在 Spring ...

  6. 设计模式在 Spring 框架中的良好应用

    在开始正文之前,请你先思考几个问题: 你项目中有使用哪些 GOF 设计模式 说一说 GOF 23 种设计模式的设计理念 说说 Spring 框架中如何实现设计模式 假设我是面试官问起了你这些面试题,你 ...

  7. 再析在spring框架中解决多数据源的问题

    在前面我写了<如何在spring框架中解决多数据源的问题>,通过设计模式中的Decorator模式在spring框架中解决多数据源的问题,得到了许多网友的关注.在与网友探讨该问题的过程中, ...

  8. Spring框架中 配置c3p0连接池 完成对数据库的访问

    开发准备: 1.导入jar包: ioc基本jar jdbcTemplate基本jar c3p0基本jar 别忘了mysql数据库驱动jar 原始程序代码:不使用配置文件方式(IOC)生成访问数据库对象 ...

  9. Spring框架中ModelAndView、Model、ModelMap区别

    原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别   注意:如果方法 ...

随机推荐

  1. 华为计算平台MDC810发布量产

    华为计算平台MDC810发布量产 塞力斯的发布会刚刚结束,会上塞力斯SF5自由远征版也确实让人眼前一亮. 全球首款4S级加速能力.1000+km续航新能源作为这款车的卖点. 续航1000+km成了最近 ...

  2. YOLOv4没交棒,但YOLOv5来了!

    YOLOv4没交棒,但YOLOv5来了! 前言 4月24日,YOLOv4来了! 5月30日,"YOLOv5"来了! 这里的 "YOLOv5" 是带有引号的,因为 ...

  3. CUDA功能和通用功能

    CUDA功能和通用功能 本文描述了类似于CUDA ufunc的对象. 为了支持CUDA程序的编程模式,CUDA Vectorize和GUVectorize无法产生常规的ufunc.而是返回类似ufun ...

  4. 浅谈Gson和fastjson使用中的坑

    相信大家在代码编写中都用过Gson和fastjson吧,用来进行 Java对象和json字符串之间的转换. 本篇文章就主要介绍博主在工作中使用这两款工具时遇到的坑和对应的解决办法. 觉得有用的可以点个 ...

  5. kali2020.4中安装nessus 8.14.0

    1.下载软件包 官网下载地址:https://www.tenable.com/downloads/nessus 2.安装nessus dpkg -i /root/Nessus-8.14.0-debia ...

  6. Python_Selenium之浏览器封装_去掉浏览器受到自动化控制横条显示及去掉是否记住密码弹窗

    封装如下: from selenium import webdriverfrom common.config_utils import configfrom selenium.webdriver.ch ...

  7. csp-s模拟测试58「Divisors」·「Market」·「Dash Speed」

    A. Divisors   大概平均下来每个数也就几千约数吧....,直接筛 B. Market 可以把时间离线下来, 考试没有想到将询问离线,用数组存算了算只能过200的点,拿了70 事实上背包后直 ...

  8. electron-ipc通信性能分析

    electron-ipc通信性能分析 electron的主进程和渲染进程间通信方案 ipc通信 借助外部存储通信(通过ipc通知其它进程去读取) 方案描述 ipc通信 使用 主进程 ==> 渲染 ...

  9. org.junit.Assert(断言)

    org.junit.Assert(断言) Assert是断言的意思,可以理解为"猜测",如果猜测错误,则抛出java.lang.AssertionError异常.  引入jar包  ...

  10. 同事内推的那位Linux C/C++后端开发同学面试没过......

    最近同事内推了一位 Linux C/C++ 后端开发的同学到我们公司面试,我是一面的面试官,很遗憾这位工作了两年的同学面试表现不是很好.我问了如下一些问题: "redis持久化机制,redi ...