Guide de Spring Retry

1. Vue d'ensemble

Spring Retry offre la possibilité de réappeler automatiquement une opération ayant échoué. Ceci est utile lorsque les erreurs peuvent être transitoires (comme un problème de réseau momentané).

Dans ce didacticiel, nous verrons les différentes façons d'utiliser Spring Retry: annotations, RetryTemplate et rappels.

2. Dépendances de Maven

Commençons par ajouter la dépendance spring-retry dans notre fichier pom.xml :

 org.springframework.retry spring-retry 1.2.5.RELEASE 

Nous devons également ajouter Spring AOP à notre projet:

 org.springframework spring-aspects 5.2.8.RELEASE 

Jetez un œil à Maven Central pour les dernières versions des dépendances spring-retry et spring-aspects.

3. Activation de Spring Retry

Pour activer Spring Retry dans une application, nous devons ajouter l' annotation @EnableRetry à notre classe @Configuration :

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

4. Utilisation de Spring Retry

4.1. @Retryable sans récupération

Pour ajouter une fonctionnalité de nouvelle tentative aux méthodes, nous pouvons utiliser l' annotation @Retryable :

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

Dans cet exemple, la nouvelle tentative est tentée lorsqu'une RuntimeException est levée .

Selon le comportement par défaut de @Retryable , la nouvelle tentative peut se produire jusqu'à trois fois, avec un délai d'une seconde entre les tentatives .

4.2. @Retryable et @Recover

Ajoutons maintenant une méthode de récupération à l'aide de l' annotation @Recover :

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

Dans cet exemple, la nouvelle tentative est tentée lorsqu'une exception SQLException est levée . L' annotation @Recover définit une méthode de récupération distincte lorsqu'une méthode @Retryable échoue avec une exception spécifiée.

Par conséquent, si la méthode retryServiceWithRecovery continue de lancer une exception SqlException après 3 tentatives, la méthode recover () sera appelée.

Le gestionnaire de récupération doit avoir le premier paramètre de type Throwable (facultatif) et le même type de retour.Les arguments suivants sont renseignés à partir de la liste des arguments de la méthode ayant échoué dans le même ordre.

4.3. Personnalisation du comportement de @ Retryable

Afin de personnaliser le comportement d'une nouvelle tentative, nous pouvons utiliser les paramètres maxAttempts et backoff :

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

Dans l'exemple ci-dessus, il y aura jusqu'à 2 tentatives et un délai de 100 millisecondes.

4.4. Utilisation des propriétés du ressort

Nous pouvons également utiliser des propriétés dans l' annotation @Retryable .

Pour illustrer cela, nous verrons comment externaliser les valeurs de delay et maxAttempts dans un fichier de propriétés .

Tout d'abord, définissons les propriétés dans un fichier appelé retryConfig. propriétés :

retry.maxAttempts=2 retry.maxDelay=100

Nous demandons ensuite à notre classe @Configuration de charger ce fichier:

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

Enfin, nous pouvons injecter les valeurs de retry.maxAttempts et retry.maxDelay dans notre définition @Retryable :

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

Veuillez noter que nous utilisons maintenant maxAttemptsExpression et delayExpression au lieu de maxAttempts et delay .

5. RetryTemplate

5.1 Réessayer les opérations

Spring Retry fournit une interface RetryOperations qui fournit un ensemble de méthodes execute () :

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

Le RetryCallback qui est un paramètre de execute () est une interface qui permet l'insertion d'une logique métier qui doit être réessayée en cas d'échec:

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

5.2. Configuration de RetryTemplate

Le RetryTemplate est une implémentation de RetryOperations . Configurons un bean RetryTemplate dans notre classe @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; } } 

Le RetryPolicy détermine quand une opération doit être réessayée.

Un SimpleRetryPolicy est utilisé pour réessayer un nombre fixe de fois. D'autre part, BackOffPolicy est utilisé pour contrôler l'interruption entre les tentatives de relance .

Enfin, un FixedBackOffPolicy s'interrompt pendant une période de temps fixe avant de continuer.

5.3. Utilisation du RetryTemplate

Pour exécuter du code avec la gestion des tentatives, nous pouvons appeler la méthode r etryTemplate.execute () :

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

Au lieu d'une classe anonyme, nous pouvons utiliser une expression lambda comme suit:

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

6. Auditeurs

Les auditeurs fournissent des rappels supplémentaires lors des tentatives. Nous pouvons les utiliser pour diverses préoccupations transversales à travers différentes tentatives.

6.1. Ajouter des rappels

Les rappels sont fournis dans une interface 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); } }

Les rappels d' ouverture et de fermeture interviennent avant et après la tentative complète, tandis que onError s'applique aux appels RetryCallback individuels .

6.2. Enregistrement de l'auditeur

Ensuite, nous enregistrons notre auditeur ( DefaultListenerSupport) dans notre bean RetryTemplate :

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

7. Test des résultats

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.