Background
In the previous article, we briefly introduced the function and simple usage of spring-retry
, but the annotation @Retryable
also has a parameter listeners
that we did not carry out. illustrate,
So in this article we introduce the usage of this parameter in detail.
Analysis
We can know from the parameter name that some listeners can be configured here. So how to configure these listeners? First we analyze the source code.
Annotation source code
We only keep the source code of this parameter and omit the others.
@Target({<!-- --> ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Retryable {<!-- --> /** * Bean names of retry listeners to use instead of default ones defined in Spring * context * @return retry listeners bean names */ String[] listeners() default {<!-- -->}; }
Note: The explanation of this parameter in the source code: the
Bean
name of the retry listener is used instead of the default name defined in theSpring
context. We can boldly guess
This is a specificBean
that needs to be defined by the developer. And the parameters that can be received are in the form of arrays, so the question is how to define them?
Processing annotation source code
As mentioned in our last article, the class that handles the annotation @Retryable
is: AnnotationAwareRetryOperationsInterceptor
, so we will find the answer we want in this class.
- Method
getListenersBeans()
Source code
private RetryListener[] getListenersBeans(String[] listenersBeanNames) {<!-- --> RetryListener[] listeners = new RetryListener[listenersBeanNames.length]; for (int i = 0; i < listeners.length; i + + ) {<!-- --> listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class); } return listeners; }
From the above code, we can know that the listener we defined must have some relationship with
RetryListener
. Next we analyze the source code of this class
RetryListener
Source code
/** * Interface for listener that can be used to add behavior to a retry. Implementations of * {@link RetryOperations} can chose to issue callbacks to an interceptor during the retry *lifecycle. */ public interface RetryListener {<!-- --> /** * Called before the first attempt in a retry. For instance, implementers can set up * state that is needed by the policies in the {@link RetryOperations}. The whole * retry can be vetted by returning false from this method, in which case a * {@link TerminatedRetryException} will be thrown. * @param <T> the type of object returned by the callback * @param <E> the type of exception it declares may be thrown * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @return true if the retry should proceed. */ <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback); /** * Called after the final attempt (successful or not). Allow the interceptor to clean * up any resource it is holding before control returns to the retry caller. * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @param throwable the last exception that was thrown by the callback. * @param <E> the exception type * @param <T> the return value */ <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); /** * Called after every unsuccessful attempt at a retry. * @param context the current {@link RetryContext}. * @param callback the current {@link RetryCallback}. * @param throwable the last exception that was thrown by the callback. * @param <T> the return value * @param <E> the exception to throw */ <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable); }
Analysis: First of all, this is an interface, so don’t think about it. Developer-defined listeners must implement this interface.
Implementation
Since the parameter listeners
can be multiple, and the input parameter is an array, we first define the listener for the two brothers. code show as below
RetryListenerBean
@Slf4j public class RetryListenerBean implements RetryListener {<!-- --> @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {<!-- --> log.info("The open method was executed"); return true; } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {<!-- --> log.info("The onError method was executed, indicating that an exception occurred"); } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {<!-- --> log.info("The close method was executed"); } }
RetryListenerTwoBean
@Slf4j public class RetryListenerTwoBean implements RetryListener {<!-- --> @Override public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {<!-- --> log.info("The second open method was executed"); return true; } @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {<!-- --> log.info("The second onError method was executed, indicating that an exception occurred"); } @Override public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {<!-- --> log.info("The second close method was executed"); } }
Analysis 1: We defined our own two listeners and configured them in the parameter
listeners
. The code is as follows:
@Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5),listeners = {<!-- -->"retryListenerBean", "retryListenerTwoBean\ "} )
Analysis 2: So how does the framework identify the listener we configured? Please see the source code
for (int i = 0; i < listeners.length; i + + ) {<!-- --> listeners[i] = this.beanFactory.getBean(listenersBeanNames[i], RetryListener.class); }
Analysis 3: In the above code, we found
beanFactory#getBean()
, then all the truth is revealed. Therefore we must define our own listener
It is managed bySpring
. So we need to configure the listener we defined.
Listener configuration
We configure the listener we defined and it is managed by Spring
. The configuration code is as follows:
@Configuration public class Config {<!-- --> @Bean("retryListenerBean") public RetryListenerBean listenerBean(){<!-- --> return new RetryListenerBean(); } @Bean("retryListenerTwoBean") public RetryListenerTwoBean listenerTwoBean(){<!-- --> return new RetryListenerTwoBean(); } }
After configuring the above code, you can obtain the instance of the listener through the name of
Bean
and the method ofgetBean
.
Test
Start the project and we use postman
for testing. The log is as follows:
2023-07-23 14:37:07.102 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.controller.RetryController: Controller request input parameter is: 222 2023-07-23 14:37:25.312 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean: The open method was executed 2023-07-23 14:37:25.313 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean: The second open method was executed 2023-07-23 14:37:30.816 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Service request input parameter is: 222 2023-07-23 14:37:31.285 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Enter the test method, the current time is: Sun Jul 23 14:37:31 CST 2023 2023-07-23 14:37:38.312 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean: The second onError method was executed, indicating that an exception occurred 2023-07-23 14:37:38.313 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean: The onError method was executed, indicating that an exception occurred 2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Service request input parameters are: 222 2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Enter the test method, the current time is: Sun Jul 23 14:37:54 CST 2023 2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean: The second onError method was executed, indicating that an exception occurred 2023-07-23 14:37:54.729 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean: The onError method was executed, indicating that an exception occurred 2023-07-23 14:38:02.498 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Service request input parameters are: 222 2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: Enter the test method, the current time is: Sun Jul 23 14:38:02 CST 2023 2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean: The second onError method was executed, indicating that an exception occurred 2023-07-23 14:38:02.499 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean: The onError method was executed, indicating that an exception occurred 2023-07-23 14:38:02.500 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.service.impl.RetryServiceImpl: callback operation after an exception occurs, the input parameter is: 222, the current time is: Sun Jul 23 14:38:02 CST 2023 2023-07-23 14:38:02.501 INFO 12932 --- [nio-8111-exec-9] o.t.s.l.bean.RetryListenerTwoBean: The second close method was executed 2023-07-23 14:38:02.502 INFO 12932 --- [nio-8111-exec-9] o.t.s.loopretry.bean.RetryListenerBean: executed the close method
From the above log, we can see that the log output in the listener we defined is executed, and the logic of the listener is executed.
Summary
From the listener code, which includes three programs open
, onError
and close
, we combine the output sequence of the log, first executing our business Before logic, execute
The open
solution is equivalent to a front-end interceptor. We can implement some front-end logical operations in this method. When encountering an abnormal situation, the onError
method will be executed. will eventually
Execute the close
method. Therefore, we can use these three methods at different stages to achieve our desired business logic.
I hope this article can help everyone understand this parameter. If you want to see the source code, you can click: Code Portal