千家信息网

Spring Boot中怎么使用Spring Retry重试框架

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,今天小编给大家分享一下Spring Boot中怎么使用Spring Retry重试框架的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读
千家信息网最后更新 2026年11月01日Spring Boot中怎么使用Spring Retry重试框架

今天小编给大家分享一下Spring Boot中怎么使用Spring Retry重试框架的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    Spring Retry 在SpringBoot 中的应用

    Spring Retry提供了自动重新调用失败的操作的功能。这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用。 从2.2.0版本开始,重试功能已从Spring Batch中撤出,成为一个独立的新库:Spring Retry

    Maven依赖

        org.springframework.retry    spring-retry    org.springframework    spring-aspects

    注解使用

    开启Retry功能

    在启动类中使用@EnableRetry注解

    package org.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.retry.annotation.EnableRetry;@SpringBootApplication@EnableRetrypublic class RetryApp {    public static void main(String[] args) {        SpringApplication.run(RetryApp.class, args);    }}

    注解@Retryable

    需要在重试的代码中加入重试注解@Retryable

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.retry.annotation.Backoff;import org.springframework.retry.annotation.Recover;import org.springframework.retry.annotation.Retryable;import org.springframework.stereotype.Service;import java.time.LocalDateTime;@Service@Slf4jpublic class RetryService {    @Retryable(value = IllegalAccessException.class)    public void service1() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException("manual exception");    }}

    默认情况下,会重试3次,间隔1秒

    我们可以从注解@Retryable中看到

    @Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Retryable {        /**         * Retry interceptor bean name to be applied for retryable method. Is mutually         * exclusive with other attributes.         * @return the retry interceptor bean name         */        String interceptor() default "";         * Exception types that are retryable. Synonym for includes(). Defaults to empty (and         * if excludes is also empty all exceptions are retried).         * @return exception types to retry        Class[] value() default {};         * Exception types that are retryable. Defaults to empty (and if excludes is also         * empty all exceptions are retried).        Class[] include() default {};         * Exception types that are not retryable. Defaults to empty (and if includes is also         * If includes is empty but excludes is not, all not excluded exceptions are retried         * @return exception types not to retry        Class[] exclude() default {};         * A unique label for statistics reporting. If not provided the caller may choose to         * ignore it, or provide a default.         *         * @return the label for the statistics        String label() default "";         * Flag to say that the retry is stateful: i.e. exceptions are re-thrown, but the         * retry policy is applied with the same policy to subsequent invocations with the         * same arguments. If false then retryable exceptions are not re-thrown.         * @return true if retry is stateful, default false        boolean stateful() default false;         * @return the maximum number of attempts (including the first failure), defaults to 3        int maxAttempts() default 3;  //默认重试次数3次         * @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3         * Overrides {@link #maxAttempts()}.         * @since 1.2        String maxAttempts_Expression() default "";         * Specify the backoff properties for retrying this operation. The default is a         * simple {@link Backoff} specification with no properties - see it's documentation         * for defaults.         * @return a backoff specification        Backoff backoff() default @Backoff(); //默认的重试中的退避策略         * Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()}         * returns true - can be used to conditionally suppress the retry. Only invoked after         * an exception is thrown. The root object for the evaluation is the last {@code Throwable}.         * Other beans in the context can be referenced.         * For example:         * 
             *  {@code "message.contains('you can retry this')"}.         * 
    * and * {@code "@someBean.shouldRetry(#root)"}. * @return the expression. String exception_Expression() default ""; * Bean names of retry listeners to use instead of default ones defined in Spring context * @return retry listeners bean names String[] listeners() default {};}
    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Backoff {        /**         * Synonym for {@link #delay()}.         *         * @return the delay in milliseconds (default 1000)         */        long value() default 1000; //默认的重试间隔1秒         * A canonical backoff period. Used as an initial value in the exponential case, and         * as a minimum value in the uniform case.         * @return the initial or canonical backoff period in milliseconds (default 1000)        long delay() default 0;         * The maximimum wait (in milliseconds) between retries. If less than the         * {@link #delay()} then the default of         * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}         * is applied.         * @return the maximum delay between retries (default 0 = ignored)        long maxDelay() default 0;         * If positive, then used as a multiplier for generating the next delay for backoff.         * @return a multiplier to use to calculate the next backoff delay (default 0 =         * ignored)        double multiplier() default 0;         * An expression evaluating to the canonical backoff period. Used as an initial value         * in the exponential case, and as a minimum value in the uniform case. Overrides         * {@link #delay()}.         * @return the initial or canonical backoff period in milliseconds.         * @since 1.2        String delay_Expression() default "";         * An expression evaluating to the maximimum wait (in milliseconds) between retries.         * If less than the {@link #delay()} then the default of         * is applied. Overrides {@link #maxDelay()}        String maxDelay_Expression() default "";         * Evaluates to a vaule used as a multiplier for generating the next delay for         * backoff. Overrides {@link #multiplier()}.         * @return a multiplier expression to use to calculate the next backoff delay (default         * 0 = ignored)        String multiplier_Expression() default "";         * In the exponential case ({@link #multiplier()} > 0) set this to true to have the         * backoff delays randomized, so that the maximum delay is multiplier times the         * previous delay and the distribution is uniform between the two values.         * @return the flag to signal randomization is required (default false)        boolean random() default false;}

    我们来运行测试代码

    package org.example;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestclass RetryServiceTest {    @Autowired    private RetryService retryService;    @Test    void testService1() throws IllegalAccessException {        retryService.service1();    }}

    运行结果如下:

    2021-01-05 19:40:41.221 INFO 3548 --- [ main] org.example.RetryService : do something... 2021-01-05T19:40:41.221763300
    2021-01-05 19:40:42.224 INFO 3548 --- [ main] org.example.RetryService : do something... 2021-01-05T19:40:42.224436500
    2021-01-05 19:40:43.225 INFO 3548 --- [ main] org.example.RetryService : do something... 2021-01-05T19:40:43.225189300

    java.lang.IllegalAccessException: manual exception    at org.example.RetryService.service1(RetryService.java:19)    at org.example.RetryService$$FastClassBySpringCGLIB$$c0995ddb.invoke()    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)    at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:91)    at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)    at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)    at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)    at org.example.RetryService$$EnhancerBySpringCGLIB$$499afa1d.service1()    at org.example.RetryServiceTest.testService1(RetryServiceTest.java:16)    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    at java.base/java.lang.reflect.Method.invoke(Method.java:566)    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

    可以看到重新执行了3次service1()方法,然后间隔是1秒,然后最后还是重试失败,所以抛出了异常

    既然我们看到了注解@Retryable中有这么多参数可以设置,那我们就来介绍几个常用的配置。

    @Retryable(include = IllegalAccessException.class, maxAttempts = 5)public void service2() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    首先是maxAttempts,用于设置重试次数

    2021-01-06 09:30:11.263 INFO 15612 --- [ main] org.example.RetryService : do something... 2021-01-06T09:30:11.263621900
    2021-01-06 09:30:12.265 INFO 15612 --- [ main] org.example.RetryService : do something... 2021-01-06T09:30:12.265629100
    2021-01-06 09:30:13.265 INFO 15612 --- [ main] org.example.RetryService : do something... 2021-01-06T09:30:13.265701
    2021-01-06 09:30:14.266 INFO 15612 --- [ main] org.example.RetryService : do something... 2021-01-06T09:30:14.266705400
    2021-01-06 09:30:15.266 INFO 15612 --- [ main] org.example.RetryService : do something... 2021-01-06T09:30:15.266733200

    java.lang.IllegalAccessException: manual exception
    ....

    从运行结果可以看到,方法执行了5次。

    下面来介绍maxAttemptsExpression的设置

    @Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "${maxAttempts}")public void service3() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    maxAttemptsExpression则可以使用表达式,比如上述就是通过获取配置中maxAttempts的值,我们可以在application.yml设置。上述其实省略掉了SpEL表达式#{....},运行结果的话可以发现方法执行了4次..

    maxAttempts: 4

    我们可以使用SpEL表达式

    @Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "#{1+1}")public void service3_1() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}@Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "#{${maxAttempts}}")//效果和上面的一样public void service3_2() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    接着我们下面来看看exceptionExpression, 一样也是写SpEL表达式

    @Retryable(value = IllegalAccessException.class, exceptionExpression = "message.contains('test')")public void service4(String exceptionMessage) throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException(exceptionMessage);}    @Retryable(value = IllegalAccessException.class, exceptionExpression = "#{message.contains('test')}")public void service4_3(String exceptionMessage) throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException(exceptionMessage);}

    上面的表达式exceptionExpression = "message.contains('test')"的作用其实是获取到抛出来exception的message(调用了getMessage()方法),然后判断message的内容里面是否包含了test字符串,如果包含的话就会执行重试。所以如果调用方法的时候传入的参数exceptionMessage中包含了test字符串的话就会执行重试。

    但这里值得注意的是, Spring Retry 1.2.5之后exceptionExpression是可以省略掉#{...}

    Since Spring Retry 1.2.5, for exceptionExpression, templated expressions (#{...}) are deprecated in favor of simple expression strings (message.contains('this can be retried')).

    使用1.2.5之后的版本运行是没有问题的

        org.springframework.retry    spring-retry    1.3.0

    但是如果使用1.2.5版本之前包括1.2.5版本的话,运行的时候会报错如下:

    2021-01-06 09:52:45.209 INFO 23220 --- [ main] org.example.RetryService : do something... 2021-01-06T09:52:45.209178200

    org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from java.lang.String to java.lang.Boolean
    at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:75)
    at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:57)
    at org.springframework.expression.common.LiteralExpression.getValue(LiteralExpression.java:106)
    at org.springframework.retry.policy.ExpressionRetryPolicy.canRetry(ExpressionRetryPolicy.java:113)
    at org.springframework.retry.support.RetryTemplate.canRetry(RetryTemplate.java:375)
    at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:304)
    at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)
    at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)
    at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
    at org.example.RetryService$$EnhancerBySpringCGLIB$$d321a75e.service4()
    at org.example.RetryServiceTest.testService4_2(RetryServiceTest.java:46)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
    Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Boolean] for value 'message.contains('test')'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value 'message.contains('test')'
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
    at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:70)
    ... 76 more
    Caused by: java.lang.IllegalArgumentException: Invalid boolean value 'message.contains('test')'
    at org.springframework.core.convert.support.StringToBooleanConverter.convert(StringToBooleanConverter.java:63)
    at org.springframework.core.convert.support.StringToBooleanConverter.convert(StringToBooleanConverter.java:31)
    at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:385)
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
    ... 78 more

    还可以在表达式中执行一个方法,前提是方法的类在spring容器中注册了,@retryService其实就是获取bean name为retryService的bean,然后调用里面的checkException方法,传入的参数为#root,它其实就是抛出来的exception对象。一样的也是可以省略#{...}

     @Retryable(value = IllegalAccessException.class, exceptionExpression = "#{@retryService.checkException(#root)}")    public void service5(String exceptionMessage) throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException(exceptionMessage);    }    @Retryable(value = IllegalAccessException.class, exceptionExpression = "@retryService.checkException(#root)")    public void service5_1(String exceptionMessage) throws IllegalAccessException {        public boolean checkException(Exception e) {        log.error("error message:{}", e.getMessage());        return true; //返回true的话表明会执行重试,如果返回false则不会执行重试

    运行结果:

    2021-01-06 13:33:52.913 INFO 23052 --- [ main] org.example.RetryService : do something... 2021-01-06T13:33:52.913404
    2021-01-06 13:33:52.981 ERROR 23052 --- [ main] org.example.RetryService : error message:test message
    2021-01-06 13:33:53.990 ERROR 23052 --- [ main] org.example.RetryService : error message:test message
    2021-01-06 13:33:53.990 INFO 23052 --- [ main] org.example.RetryService : do something... 2021-01-06T13:33:53.990947400
    2021-01-06 13:33:53.990 ERROR 23052 --- [ main] org.example.RetryService : error message:test message
    2021-01-06 13:33:54.992 ERROR 23052 --- [ main] org.example.RetryService : error message:test message
    2021-01-06 13:33:54.992 INFO 23052 --- [ main] org.example.RetryService : do something... 2021-01-06T13:33:54.992342900

    当然还有更多表达式的用法了...

    @Retryable(exceptionExpression = "#{#root instanceof T(java.lang.IllegalAccessException)}") //判断exception的类型    public void service5_2(String exceptionMessage) {        log.info("do something... {}", LocalDateTime.now());        throw new NullPointerException(exceptionMessage);    }    @Retryable(exceptionExpression = "#root instanceof T(java.lang.IllegalAccessException)")    public void service5_3(String exceptionMessage) {        log.info("do something... {}", LocalDateTime.now());        throw new NullPointerException(exceptionMessage);    }
     @Retryable(exceptionExpression = "myMessage.contains('test')") //查看自定义的MyException中的myMessage的值是否包含test字符串    public void service5_4(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage); //自定义的exception    }    @Retryable(exceptionExpression = "#root.myMessage.contains('test')")  //和上面service5_4方法的效果一样    public void service5_5(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage);    }
    package org.example;import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class MyException extends Exception {    private String myMessage;    public MyException(String myMessage) {        this.myMessage = myMessage;    }}

    下面再来看看另一个配置exclude

     @Retryable(exclude = MyException.class)    public void service6(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage);    }

    这个exclude属性可以帮我们排除一些我们不想重试的异常

    最后我们来看看这个backoff 重试等待策略, 默认使用@Backoff注解。

    我们先来看看这个@Backoff的value属性,用于设置重试间隔

    @Retryable(value = IllegalAccessException.class,            backoff = @Backoff(value = 2000))    public void service7() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果可以看出来重试的间隔为2秒

    2021-01-06 14:47:38.036 INFO 21116 --- [ main] org.example.RetryService : do something... 2021-01-06T14:47:38.036732600
    2021-01-06 14:47:40.038 INFO 21116 --- [ main] org.example.RetryService : do something... 2021-01-06T14:47:40.037753600
    2021-01-06 14:47:42.046 INFO 21116 --- [ main] org.example.RetryService : do something... 2021-01-06T14:47:42.046642900
    java.lang.IllegalAccessException
    at org.example.RetryService.service7(RetryService.java:113)
    ...

    接下来介绍@Backoff的delay属性,它与value属性不能共存,当delay不设置的时候会去读value属性设置的值,如果delay设置的话则会忽略value属性

    @Retryable(value = IllegalAccessException.class,            backoff = @Backoff(value = 2000,delay = 500))    public void service8() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果可以看出,重试的时间间隔为500ms

    2021-01-06 15:22:42.271 INFO 13512 --- [ main] org.example.RetryService : do something... 2021-01-06T15:22:42.271504800
    2021-01-06 15:22:42.772 INFO 13512 --- [ main] org.example.RetryService : do something... 2021-01-06T15:22:42.772234900
    2021-01-06 15:22:43.273 INFO 13512 --- [ main] org.example.RetryService : do something... 2021-01-06T15:22:43.273246700
    java.lang.IllegalAccessException
    at org.example.RetryService.service8(RetryService.java:121)

    接下来我们来看``@Backoff的multiplier`的属性, 指定延迟倍数, 默认为0。

    @Retryable(value = IllegalAccessException.class,maxAttempts = 4,            backoff = @Backoff(delay = 2000, multiplier = 2))    public void service9() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    multiplier设置为2,则表示第一次重试间隔为2s,第二次为4秒,第三次为8s

    运行结果如下:

    2021-01-06 15:58:07.458 INFO 23640 --- [ main] org.example.RetryService : do something... 2021-01-06T15:58:07.458245500
    2021-01-06 15:58:09.478 INFO 23640 --- [ main] org.example.RetryService : do something... 2021-01-06T15:58:09.478681300
    2021-01-06 15:58:13.478 INFO 23640 --- [ main] org.example.RetryService : do something... 2021-01-06T15:58:13.478921900
    2021-01-06 15:58:21.489 INFO 23640 --- [ main] org.example.RetryService : do something... 2021-01-06T15:58:21.489240600
    java.lang.IllegalAccessException
    at org.example.RetryService.service9(RetryService.java:128)
    ...

    接下来我们来看看这个@Backoff的maxDelay属性,设置最大的重试间隔,当超过这个最大的重试间隔的时候,重试的间隔就等于maxDelay的值

    @Retryable(value = IllegalAccessException.class,maxAttempts = 4,            backoff = @Backoff(delay = 2000, multiplier = 2,maxDelay = 5000))    public void service10() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果:

    2021-01-06 16:12:37.377 INFO 5024 --- [ main] org.example.RetryService : do something... 2021-01-06T16:12:37.377616100
    2021-01-06 16:12:39.381 INFO 5024 --- [ main] org.example.RetryService : do something... 2021-01-06T16:12:39.381299400
    2021-01-06 16:12:43.382 INFO 5024 --- [ main] org.example.RetryService : do something... 2021-01-06T16:12:43.382169500
    2021-01-06 16:12:48.396 INFO 5024 --- [ main] org.example.RetryService : do something... 2021-01-06T16:12:48.396327600
    java.lang.IllegalAccessException
    at org.example.RetryService.service10(RetryService.java:135)

    可以最后的最大重试间隔是5秒

    注解@Recover

    当@Retryable方法重试失败之后,最后就会调用@Recover方法。用于@Retryable失败时的"兜底"处理方法。 @Recover的方法必须要与@Retryable注解的方法保持一致,第一入参为要重试的异常,其他参数与@Retryable保持一致,返回值也要一样,否则无法执行!

    @Retryable(value = IllegalAccessException.class)    public void service11() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }    @Recover    public void recover11(IllegalAccessException e) {        log.info("service retry after Recover => {}", e.getMessage());    //=========================    @Retryable(value = ArithmeticException.class)    public int service12() throws IllegalAccessException {        return 1 / 0;    public int recover12(ArithmeticException e) {        return 0;    public int service13(String message) throws IllegalAccessException {        log.info("do something... {},{}", message, LocalDateTime.now());    public int recover13(ArithmeticException e, String message) {        log.info("{},service retry after Recover => {}", message, e.getMessage());

    注解@CircuitBreaker

    熔断模式:指在具体的重试机制下失败后打开断路器,过了一段时间,断路器进入半开状态,允许一个进入重试,若失败再次进入断路器,成功则关闭断路器,注解为@CircuitBreaker,具体包括熔断打开时间、重置过期时间

    // openTimeout时间范围内失败maxAttempts次数后,熔断打开resetTimeout时长         @CircuitBreaker(openTimeout = 1000, resetTimeout = 3000, value = NullPointerException.class)    public void circuitBreaker(int num) {        log.info(" 进入断路器方法num={}", num);        if (num > 8) return;        Integer n = null;        System.err.println(1 / n);    }    @Recover    public void recover(NullPointerException e) {        log.info("service retry after Recover => {}", e.getMessage());    }

    测试方法

    @Test    public void testCircuitBreaker() throws InterruptedException {        System.err.println("尝试进入断路器方法,并触发异常...");        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        System.err.println("在openTimeout 1秒之内重试次数为2次,未达到触发熔断, 断路器依然闭合...");        TimeUnit.SECONDS.sleep(1);        System.err.println("超过openTimeout 1秒之后, 因为未触发熔断,所以重试次数重置,可以正常访问...,继续重试3次方法...");        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        System.err.println("在openTimeout 1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法...");        retryService.circuitBreaker(1);        TimeUnit.SECONDS.sleep(2);        retryService.circuitBreaker(9);        TimeUnit.SECONDS.sleep(3);        System.err.println("超过resetTimeout 3秒之后,断路器重新闭合...,可以正常访问");        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);    }

    运行结果:

    尝试进入断路器方法,并触发异常...
    2021-01-07 21:44:20.842 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=1
    2021-01-07 21:44:20.844 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    2021-01-07 21:44:20.845 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=1
    2021-01-07 21:44:20.845 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    2021-01-07 21:44:20.845 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    2021-01-07 21:44:20.845 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    在openTimeout 1秒之内重试次数为2次,未达到触发熔断, 断路器依然闭合...
    超过openTimeout 1秒之后, 因为未触发熔断,所以重试次数重置,可以正常访问...,继续重试3次方法...
    2021-01-07 21:44:21.846 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=1
    2021-01-07 21:44:21.847 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    2021-01-07 21:44:21.847 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=1
    2021-01-07 21:44:21.847 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    2021-01-07 21:44:21.847 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=1
    2021-01-07 21:44:21.848 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    在openTimeout 1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法...
    2021-01-07 21:44:21.848 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    2021-01-07 21:44:23.853 INFO 7464 --- [ main] org.example.RetryService : service retry after Recover => null
    超过resetTimeout 3秒之后,断路器重新闭合...,可以正常访问
    2021-01-07 21:44:26.853 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    2021-01-07 21:44:26.854 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    2021-01-07 21:44:26.855 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    2021-01-07 21:44:26.855 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9
    2021-01-07 21:44:26.856 INFO 7464 --- [ main] org.example.RetryService : 进入断路器方法num=9

    RetryTemplate

    RetryTemplate配置

    package org.example;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.retry.backoff.FixedBackOffPolicy;import org.springframework.retry.policy.SimpleRetryPolicy;import org.springframework.retry.support.RetryTemplate;@Configurationpublic class AppConfig {       @Bean    public RetryTemplate retryTemplate() {        RetryTemplate retryTemplate = new RetryTemplate();        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); //设置重试策略        retryPolicy.setMaxAttempts(2);        retryTemplate.setRetryPolicy(retryPolicy);        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); //设置退避策略        fixedBackOffPolicy.setBackOffPeriod(2000L);        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);                        return retryTemplate;    }}

    可以看到这些配置跟我们直接写注解的方式是差不多的,这里就不过多的介绍了。。

    使用RetryTemplate

    package org.example;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.retry.RetryCallback;import org.springframework.retry.RetryContext;import org.springframework.retry.support.RetryTemplate;@SpringBootTestpublic class RetryTemplateTest {    @Autowired    private RetryTemplate retryTemplate;    private RetryTemplateService retryTemplateService;    @Test    void test1() throws IllegalAccessException {        retryTemplate.execute(new RetryCallback() {            @Override            public Object doWithRetry(RetryContext context) throws IllegalAccessException {                 retryTemplateService.service1();                return null;            }        });    }    void test2() throws IllegalAccessException {                retryTemplateService.service1();        }, new RecoveryCallback() {            public Object recover(RetryContext context) throws Exception {                log.info("RecoveryCallback....");}

    RetryOperations定义重试的API,RetryTemplate是API的模板模式实现,实现了重试和熔断。提供的API如下:

    package org.springframework.retry;import org.springframework.retry.support.DefaultRetryState;/** * Defines the basic set of operations implemented by {@link RetryOperations} to execute * operations with configurable retry behaviour. * * @author Rob Harrop * @author Dave Syer */public interface RetryOperations {        /**         * Execute the supplied {@link RetryCallback} with the configured retry semantics. See         * implementations for configuration details.         * @param  the return value         * @param retryCallback the {@link RetryCallback}         * @param  the exception to throw         * @return the value returned by the {@link RetryCallback} upon successful invocation.         * @throws E any {@link Exception} raised by the {@link RetryCallback} upon         * unsuccessful retry.         * @throws E the exception thrown         */         T execute(RetryCallback retryCallback) throws E;         * Execute the supplied {@link RetryCallback} with a fallback on exhausted retry to         * the {@link RecoveryCallback}. See implementations for configuration details.         * @param recoveryCallback the {@link RecoveryCallback}         * @param retryCallback the {@link RetryCallback} {@link RecoveryCallback} upon         * @param  the type to return         * @param  the type of the exception         * @return the value returned by the {@link RetryCallback} upon successful invocation,         * and that returned by the {@link RecoveryCallback} otherwise.         * @throws E any {@link Exception} raised by the unsuccessful retry.         T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback)                        throws E;         * A simple stateful retry. Execute the supplied {@link RetryCallback} with a target         * object for the attempt identified by the {@link DefaultRetryState}. Exceptions         * thrown by the callback are always propagated immediately so the state is required         * to be able to identify the previous attempt, if there is one - hence the state is         * required. Normal patterns would see this method being used inside a transaction,         * where the callback might invalidate the transaction if it fails.         *         * See implementations for configuration details.         * @param retryState the {@link RetryState}         * @param  the type of the return value         * @param  the type of the exception to return         * @throws E any {@link Exception} raised by the {@link RecoveryCallback}.         * @throws ExhaustedRetryException if the last attempt for this state has already been         * reached         T execute(RetryCallback retryCallback, RetryState retryState)                        throws E, ExhaustedRetryException;         * A stateful retry with a recovery path. Execute the supplied {@link RetryCallback}         * with a fallback on exhausted retry to the {@link RecoveryCallback} and a target         * object for the retry attempt identified by the {@link DefaultRetryState}.         * @param  the return value type         * @param  the exception type         * @see #execute(RetryCallback, RetryState)         * @throws E any {@link Exception} raised by the {@link RecoveryCallback} upon         T execute(RetryCallback retryCallback, RecoveryCallback recoveryCallback,                        RetryState retryState) throws E;}

    下面主要介绍一下RetryTemplate配置的时候,需要设置的重试策略和退避策略

    RetryPolicy

    RetryPolicy是一个接口, 然后有很多具体的实现,我们先来看看它的接口中定义了什么方法

    package org.springframework.retry;import java.io.Serializable;/** * A {@link RetryPolicy} is responsible for allocating and managing resources needed by * {@link RetryOperations}. The {@link RetryPolicy} allows retry operations to be aware of * their context. Context can be internal to the retry framework, e.g. to support nested * retries. Context can also be external, and the {@link RetryPolicy} provides a uniform * API for a range of different platforms for the external context. * * @author Dave Syer */public interface RetryPolicy extends Serializable {        /**         * @param context the current retry status         * @return true if the operation can proceed         */        boolean canRetry(RetryContext context);          * Acquire resources needed for the retry operation. The callback is passed in so that         * marker interfaces can be used and a manager can collaborate with the callback to         * set up some state in the status token.         * @param parent the parent context if we are in a nested retry.         * @return a {@link RetryContext} object specific to this policy.         *        RetryContext open(RetryContext parent);         * @param context a retry status created by the {@link #open(RetryContext)} method of         * this policy.        void close(RetryContext context);         * Called once per retry attempt, after the callback fails.         * @param context the current status object.         * @param throwable the exception to throw        void registerThrowable(RetryContext context, Throwable throwable);}

    我们来看看他有什么具体的实现类

    • SimpleRetryPolicy 默认最多重试3次

    • TimeoutRetryPolicy 默认在1秒内失败都会重试

    • ExpressionRetryPolicy 符合表达式就会重试

    • CircuitBreakerRetryPolicy 增加了熔断的机制,如果不在熔断状态,则允许重试

    • CompositeRetryPolicy 可以组合多个重试策略

    • NeverRetryPolicy 从不重试(也是一种重试策略哈)

    • AlwaysRetryPolicy 总是重试

    • 等等...

    BackOffPolicy

    看一下退避策略,退避是指怎么去做下一次的重试,在这里其实就是等待多长时间。

    • FixedBackOffPolicy 默认固定延迟1秒后执行下一次重试

    • ExponentialBackOffPolicy 指数递增延迟执行重试,默认初始0.1秒,系数是2,那么下次延迟0.2秒,再下次就是延迟0.4秒,如此类推,最大30秒。

    • ExponentialRandomBackOffPolicy 在上面那个策略上增加随机性

    • UniformRandomBackOffPolicy 这个跟上面的区别就是,上面的延迟会不停递增,这个只会在固定的区间随机

    • StatelessBackOffPolicy 这个说明是无状态的,所谓无状态就是对上次的退避无感知,从它下面的子类也能看出来

    • 等等...

    RetryListener

    listener可以监听重试,并执行对应的回调方法

    package org.springframework.retry;/** * 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. * * @author Dave Syer */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  the type of object returned by the callback         * @param  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.         */         boolean open(RetryContext context, RetryCallback 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 throwable the last exception that was thrown by the callback.         * @param  the exception type         * @param  the return value         void close(RetryContext context, RetryCallback callback, Throwable throwable);         * Called after every unsuccessful attempt at a retry.         * @param  the exception to throw         void onError(RetryContext context, RetryCallback callback, Throwable throwable);}

    使用如下:

    自定义一个Listener

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.retry.RetryCallback;import org.springframework.retry.RetryContext;import org.springframework.retry.listener.RetryListenerSupport;@Slf4jpublic class DefaultListenerSupport extends RetryListenerSupport {    @Override    public  void close(RetryContext context,                                               RetryCallback callback, Throwable throwable) {        log.info("onClose");        super.close(context, callback, throwable);    }    public  void onError(RetryContext context,                                                 RetryCallback callback, Throwable throwable) {        log.info("onError");        super.onError(context, callback, throwable);    public  boolean open(RetryContext context,                                                 RetryCallback callback) {        log.info("onOpen");        return super.open(context, callback);}

    把listener设置到retryTemplate中

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.retry.backoff.FixedBackOffPolicy;import org.springframework.retry.policy.SimpleRetryPolicy;import org.springframework.retry.support.RetryTemplate;@Configuration@Slf4jpublic class AppConfig {    @Bean    public RetryTemplate retryTemplate() {        RetryTemplate retryTemplate = new RetryTemplate();        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); //设置重试策略        retryPolicy.setMaxAttempts(2);        retryTemplate.setRetryPolicy(retryPolicy);        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); //设置退避策略        fixedBackOffPolicy.setBackOffPeriod(2000L);        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);        retryTemplate.registerListener(new DefaultListenerSupport()); //设置retryListener        return retryTemplate;    }}

    测试结果:

    2021-01-08 10:48:05.663 INFO 20956 --- [ main] org.example.DefaultListenerSupport : onOpen
    2021-01-08 10:48:05.663 INFO 20956 --- [ main] org.example.RetryTemplateService : do something...
    2021-01-08 10:48:05.663 INFO 20956 --- [ main] org.example.DefaultListenerSupport : onError
    2021-01-08 10:48:07.664 INFO 20956 --- [ main] org.example.RetryTemplateService : do something...
    2021-01-08 10:48:07.664 INFO 20956 --- [ main] org.example.DefaultListenerSupport : onError
    2021-01-08 10:48:07.664 INFO 20956 --- [ main] org.example.RetryTemplateTest : RecoveryCallback....
    2021-01-08 10:48:07.664 INFO 20956 --- [ main] org.example.DefaultListenerSupport : onClose

    以上就是"Spring Boot中怎么使用Spring Retry重试框架"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

    方法 断路器 注解 策略 运行 结果 次数 就是 属性 表达式 时间 面的 延迟 配置 时候 最大 参数 版本 状态 知识 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司 广州紫鲸互联网科技有限公司 中国互联网科技工作委员会毛军 科技股互联网 互联网电商科技 智能科技公司是互联网公司吗 北京互联网科技 无线互联网科技 唐信互联网科技 大连 互联网经济与金融科技 知域互联网科技有限公司大骗子 崇明区互联网教育科技售后服务 四川慕妮娅互联网科技有限公司 元气玛特互联网科技公司是外包吗 电子商务与互联网科技的关系 互联网与数字科技关系 下一代互联网重大应用科技园 吉林省码上互联网科技电话 盐城市攀科互联网科技有限公司 东风日产互联网科技公司叫什么 圆梦互联网科技 互联网扶贫科技 中国 科技 互联网 互联网科技集团 互联网科技公司做什么 金融科技与互联网金融相同和不同 迪庆互联网科技招生 徐州市互联网科技有限公司 上海互联网科技公司ppt 科技互联网专业务虚会材料
    0