Guía de reintento de primavera

1. Información general

Spring Retry ofrece la posibilidad de volver a invocar automáticamente una operación fallida. Esto es útil cuando los errores pueden ser transitorios (como una falla de red momentánea).

En este tutorial, veremos las diversas formas de usar Spring Retry: anotaciones, RetryTemplate y devoluciones de llamada.

2. Dependencias de Maven

Comencemos agregando la dependencia spring-retry en nuestro archivo pom.xml :

 org.springframework.retry spring-retry 1.2.5.RELEASE 

También necesitamos agregar Spring AOP a nuestro proyecto:

 org.springframework spring-aspects 5.2.8.RELEASE 

Eche un vistazo a Maven Central para conocer las últimas versiones de las dependencias Spring-Retry y Spring-Aspect.

3. Habilitación de Spring Retry

Para habilitar Spring Retry en una aplicación, necesitamos agregar la anotación @EnableRetry a nuestra clase @Configuration :

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

4. Uso de Spring Retry

4.1. @Recuperable sin recuperación

Para agregar la funcionalidad de reintento a los métodos, podemos usar la anotación @Retryable :

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

En este ejemplo, se intenta el reintento cuando se lanza una RuntimeException .

Según el comportamiento predeterminado de @Retryable , el reintento puede ocurrir hasta tres veces, con un retraso de un segundo entre reintentos .

4.2. @Retryable y @Recover

Ahora agreguemos un método de recuperación usando la anotación @Recover :

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

En este ejemplo, se intenta el reintento cuando se lanza una SQLException . La anotación @Recover define un método de recuperación independiente cuando un método @Retryable falla con una excepción especificada.

En consecuencia, si el método retryServiceWithRecovery sigue lanzando una SqlException después de 3 intentos, se llamará al método recovery () .

El controlador de recuperación debe tener el primer parámetro de tipo Throwable (opcional) y el mismo tipo de retorno.Los siguientes argumentos se completan de la lista de argumentos del método fallido en el mismo orden.

4.3. Personalización del comportamiento de @ Retryable

Para personalizar el comportamiento de un reintento, podemos usar los parámetros maxAttempts y backoff :

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

En el ejemplo anterior, habrá hasta 2 intentos y un retraso de 100 milisegundos.

4.4. Usar propiedades de primavera

También podemos usar propiedades en la anotación @Retryable .

Para demostrar esto, veremos cómo externalizar los valores de delay y maxAttempts en un archivo de propiedades .

Primero, definamos las propiedades en un archivo llamado retryConfig. propiedades :

retry.maxAttempts=2 retry.maxDelay=100

Luego le indicamos a nuestra clase @Configuration que cargue este archivo:

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

Finalmente, podemos inyectar los valores de retry.maxAttempts y retry.maxDelay en nuestra definición de @Retryable :

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

Tenga en cuenta que ahora estamos usando maxAttemptsExpression y delayExpression en lugar de maxAttempts y delay .

5. RetryTemplate

5.1 Reintentar operaciones

Spring Retry proporciona la interfaz RetryOperations que proporciona un conjunto de métodos execute () :

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

El RetryCallback que es un parámetro de la ejecutar () es una interfaz que permite la inserción de lógica de negocio que necesita ser juzgados en caso de fallo:

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

5.2. Configuración de RetryTemplate

El RetryTemplate es una implementación de las RetryOperations . Configuremos un bean RetryTemplate en nuestra clase @Configuration :

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

El RetryPolicy determina cuando una operación debe ser juzgado.

Una SimpleRetryPolicy se usa para reintentar un número fijo de veces. Por otro lado, BackOffPolicy se utiliza para controlar el retroceso entre reintentos.

Finalmente, FixedBackOffPolicy se detiene durante un período de tiempo fijo antes de continuar.

5.3. Usando la RetryTemplate

Para ejecutar código con manejo de reintentos, podemos llamar al método r etryTemplate.execute () :

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

En lugar de una clase anónima, podemos usar una expresión lambda de la siguiente manera:

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

6. Oyentes

Los oyentes proporcionan devoluciones de llamada adicionales en los reintentos. Podemos utilizarlos para diversas preocupaciones transversales en diferentes reintentos.

6.1. Agregar devoluciones de llamada

Las devoluciones de llamada se proporcionan en una interfaz RetryListener :

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

Las devoluciones de llamada de apertura y cierre se producen antes y después del reintento completo, mientras que onError se aplica a las llamadas individuales de RetryCallback .

6.2. Registro del oyente

A continuación, registramos nuestro oyente ( DefaultListenerSupport) en nuestro bean RetryTemplate :

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

7. Prueba de los resultados

To conclude our example, let's verify the results:

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

As we can see from the test logs, the RetryTemplate and the RetryListener have been properly configured:

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

8. Conclusion

In this article, we have seen how to use Spring Retry using annotations, the RetryTemplate, and callbacks listeners.

The source code for the examples is available over on GitHub.