Spring框架中一个有用的小组件之Spring Retry组件详解
作者:码农熊猫 发布时间:2021-05-27 10:07:40
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
的行为
为了自定义重试的行为,我们可以使用参数maxAttempts
和backoff
:
@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。
为了演示这一点,我们将看到如何将delay
和maxAttempts
的值外部化到一个properties文件中。
首先,让我们在名为retryConfig.properties
的文件中定义属性:
retry.maxAttempts=2
retry.maxDelay=100
然后我们指示@Configuration
类加载这个文件:
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }
// ...
最后,我们可以在@Retryable
的定义中注入retry.maxAttempts
和retry.maxDelay
的值:
@Service
public interface MyService {
@Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
void retryServiceWithExternalizedConfiguration(String sql) throws SQLException;
}
请注意,我们现在使用的是maxAttemptsExpression
和delayExpression
而不是maxAttempts
和delay
。
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
配置
RetryTemplate
是RetryOperations
的一个实现。
让我们在@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);
}
}
open
和close
的回调在整个重试之前和之后执行,而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;
});
}
}
从测试日志中可以看出,我们已经正确配置了RetryTemplate
和RetryListener
:
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://www.cnblogs.com/eyougo/p/15043404.html
猜你喜欢
- 前言在Spring Boot的自动配置中经常看到@ConditionalOnProperty注解的使用,本篇文章带大家来了解一下该注解的功能
- SpringBoot遇到的坑@Qualifier报红今天写项目的时候@Qualifier一直报红,排查半天后面才知道原来是idea生成项目的
- 这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。1. Service层的抽取  
- 本文实例为大家分享了Java实现简单幸运抽奖的具体代码,供大家参考,具体内容如下代码模块:User类:package test1;publi
- Maven Release当我们的项目达到了当前的目标,在经过检测后不需要改变。这时我们就需要将SNAPSHOT版本打包成RELEASE版本
- 你知道String、StringBuilder、Stringbuffer的区别吗?当你创建字符串的时候,有考虑过该使用哪个吗?别急,这篇文章
- Unity脚本自动添加头部注释就是创建.cs文件时自动添加作者名,创建时间,文件名等看示例,我们在Unity的project面板下先创建一个
- 本文实例为大家分享了java实现2048小游戏的具体代码,供大家参考,具体内容如下效果图:游戏介绍:1.2048是一款益智类小游戏,刚开始随
- 这几天自己研究了关于地手机上面开发安卓地图的问题,发现百度官方示例demo讲解百度持续定位方面还是讲解的有些不清楚,本人研究了几次之后将其弄
- FeignClient脱离eureka自定义URL需求Spring Cloud环境中的FeignClient有时候需要调用特定主机的接口,但
- 前言Java作为一种平台无关性的语言,其主要依靠于Java虚拟机——JVM,我们写好的代码会被编译成class文件,再由JVM进行加载、解析
- 目录一、图示二、链表的概念及结构三、单链表的实现四、完整代码的展示一、图示二、链表的概念及结构 链表是一种物理存储结构上非连续存储结构,数据
- 项目中经常会使用到一对多的查询场景,但是PageHelper对这种嵌套查询的支持不够,如果是一对多的列表查询,返回的分页结果是不对的参考Gi
- 今天提交代码的时候突然发现IDEA的SVN面板Version Control下面多出来这么一句话:顿时心里咯噔一下,这肯定不是好事啊。果然,
- 自定义starterSpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进 starter,应用
- 需求有时候我们想快速通过http访问本地的一些资源,但是安装一些web服务器又很费时和浪费资源,而且也不是长期使用的。这时候我们可以启动一个
- 建库建表DROP DATABASE IF EXISTS mp;CREATE DATABASE mp DEFAULT CHARACTER SE
- 在使用springmvc的时候,后台@RequestBody接受的是一个json格式的字符串,一定是一个字符串。我们可以通过@Request
- Java读取json数据并存入数据库1. pom依赖<dependency> &nbs
- static关键字在Java中,static是静态修饰关键字。用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性