首页 >> 中医养生

Spring Boot中使用Spring-Retry遗漏框架

发布时间:2025年09月18日 12:19

*/ Class[] include() default {}; /** * Exception types that are not retryable. Defaults to empty (and if includes is also * empty all exceptions are retried). * 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 maxAttemptsExpression() 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. * @since 1.2 */ String exceptionExpression() 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 delayExpression() default ""; /** * An expression evaluating to 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. Overrides {@link #maxDelay()} * * @return the maximum delay between retries (default 0 = ignored) * @since 1.2 */ String maxDelayExpression() 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) * @since 1.2 */ String multiplierExpression() 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.2217633002021-01-05 19:40:42.224 INFO 3548 那是 [ main] org.example.RetryService : do something... 2021-01-05T19:40:42.2244365002021-01-05 19:40:43.225 INFO 3548 那是 [ main] org.example.RetryService : do something... 2021-01-05T19:40:43.225189300java.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$CglibMethodInvocation.proceed(CglibAopProxy.java:747) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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 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.2636219002021-01-06 09:30:12.265 INFO 15612 那是 [ main] org.example.RetryService : do something... 2021-01-06T09:30:12.2656291002021-01-06 09:30:13.265 INFO 15612 那是 [ main] org.example.RetryService : do something... 2021-01-06T09:30:13.2657012021-01-06 09:30:14.266 INFO 15612 那是 [ main] org.example.RetryService : do something... 2021-01-06T09:30:14.2667054002021-01-06 09:30:15.266 INFO 15612 那是 [ main] org.example.RetryService : do something... 2021-01-06T09:30:15.266733200java.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.209178200org.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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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.ThrowableCollector.execute(ThrowableCollector.java:73) 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 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 moreCaused 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 { log.info("do something... {}", LocalDateTime.now()); throw new IllegalAccessException(exceptionMessage); } 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.9134042021-01-06 13:33:52.981 ERROR 23052 那是 [ main] org.example.RetryService : error message:test message2021-01-06 13:33:53.990 ERROR 23052 那是 [ main] org.example.RetryService : error message:test message2021-01-06 13:33:53.990 INFO 23052 那是 [ main] org.example.RetryService : do something... 2021-01-06T13:33:53.9909474002021-01-06 13:33:53.990 ERROR 23052 那是 [ main] org.example.RetryService : error message:test message2021-01-06 13:33:54.992 ERROR 23052 那是 [ main] org.example.RetryService : error message:test message2021-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.0367326002021-01-06 14:47:40.038 INFO 21116 那是 [ main] org.example.RetryService : do something... 2021-01-06T14:47:40.0377536002021-01-06 14:47:42.046 INFO 21116 那是 [ main] org.example.RetryService : do something... 2021-01-06T14:47:42.046642900java.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.2715048002021-01-06 15:22:42.772 INFO 13512 那是 [ main] org.example.RetryService : do something... 2021-01-06T15:22:42.7722349002021-01-06 15:22:43.273 INFO 13512 那是 [ main] org.example.RetryService : do something... 2021-01-06T15:22:43.273246700java.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.4582455002021-01-06 15:58:09.478 INFO 23640 那是 [ main] org.example.RetryService : do something... 2021-01-06T15:58:09.4786813002021-01-06 15:58:13.478 INFO 23640 那是 [ main] org.example.RetryService : do something... 2021-01-06T15:58:13.4789219002021-01-06 15:58:21.489 INFO 23640 那是 [ main] org.example.RetryService : do something... 2021-01-06T15:58:21.489240600java.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.3776161002021-01-06 16:12:39.381 INFO 5024 那是 [ main] org.example.RetryService : do something... 2021-01-06T16:12:39.3812994002021-01-06 16:12:43.382 INFO 5024 那是 [ main] org.example.RetryService : do something... 2021-01-06T16:12:43.3821695002021-01-06 16:12:48.396 INFO 5024 那是 [ main] org.example.RetryService : do something... 2021-01-06T16:12:48.396327600java.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 { log.info("do something... {}", LocalDateTime.now()); return 1 / 0; } @Recover public int recover12(ArithmeticException e) { log.info("service retry after Recover => {}", e.getMessage()); return 0; } //========================= @Retryable(value = ArithmeticException.class) public int service13(String message) throws IllegalAccessException { log.info("do something... {},{}", message, LocalDateTime.now()); return 1 / 0; } @Recover public int recover13(ArithmeticException e, String message) { log.info("{},service retry after Recover => {}", message, e.getMessage()); return 0; }释义@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=12021-01-07 21:44:20.844 INFO 7464 那是 [ main] org.example.RetryService : service retry after Recover => null2021-01-07 21:44:20.845 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=12021-01-07 21:44:20.845 INFO 7464 那是 [ main] org.example.RetryService : service retry after Recover => null2021-01-07 21:44:20.845 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=92021-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=12021-01-07 21:44:21.847 INFO 7464 那是 [ main] org.example.RetryService : service retry after Recover => null2021-01-07 21:44:21.847 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=12021-01-07 21:44:21.847 INFO 7464 那是 [ main] org.example.RetryService : service retry after Recover => null2021-01-07 21:44:21.847 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=12021-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 => null2021-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=92021-01-07 21:44:26.854 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=92021-01-07 21:44:26.855 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=92021-01-07 21:44:26.855 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=92021-01-07 21:44:26.856 INFO 7464 那是 [ main] org.example.RetryService : 转至继电器工具num=9RetryTemplateRetryTemplate装配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; }}

可以认出这些装配跟我们必要撰写释义的方式是多于的,这里就不过多的参考了。。

用作RetryTemplatepackage 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; @Autowired private RetryTemplateService retryTemplateService; @Test void test1() throws IllegalAccessException { retryTemplate.execute(new RetryCallback() { @Override public Object doWithRetry(RetryContext context) throws IllegalAccessException { retryTemplateService.service1(); return null; } }); } @Test void test2() throws IllegalAccessException { retryTemplate.execute(new RetryCallback() { @Override public Object doWithRetry(RetryContext context) throws IllegalAccessException { retryTemplateService.service1(); return null; } }, new RecoveryCallback() { @Override public Object recover(RetryContext context) throws Exception { log.info("RecoveryCallback...."); return null; } }); }}

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 retryCallback the {@link RetryCallback} * @param retryState the {@link RetryState} * @param the type of the return value * @param the type of the exception to return * @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 {@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 recoveryCallback the {@link RecoveryCallback} * @param retryState the {@link RetryState} * @param retryCallback the {@link RetryCallback} * @param the return value type * @param the exception type * @see #execute(RetryCallback, RetryState) * @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 {@link RecoveryCallback} upon * unsuccessful retry. */ 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 context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @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 context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @param throwable the last exception that was thrown by the callback. * @param the return value * @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); } @Override public void onError(RetryContext context, RetryCallback callback, Throwable throwable) { log.info("onError"); super.onError(context, callback, throwable); } @Override 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 : onOpen2021-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 : onError2021-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 : onError2021-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 Retry 在SpringBoot 当中的领域 - ityml - 博客园

南宁白癜风医院排行
四川白癜风医院哪家最好
广东妇科医院挂号咨询
南昌男科医院去哪家好
苏州看白癜风哪间医院好
中风
乳痛症
新疆整形美容
急支糖浆能给孩子喝吗
急支糖浆治什么咳嗽

上一篇: 她家绝户,财产早晚你的:听到凤凰男双亲的话,女人让她付出代价

下一篇: 《无敌对无敌7》:沈腾圆梦宋亚轩生日愿望,开始走温情路线

友情链接