Guida a Spring Retry

1. Panoramica

Spring Retry offre la possibilità di richiamare automaticamente un'operazione non riuscita. Questo è utile quando gli errori possono essere temporanei (come un guasto momentaneo della rete).

In questo tutorial vedremo i vari modi per utilizzare Spring Retry: annotations, RetryTemplate e callback.

2. Dipendenze di Maven

Cominciamo aggiungendo la dipendenza Spring- Retry nel nostro file pom.xml :

 org.springframework.retry spring-retry 1.2.5.RELEASE 

Dobbiamo anche aggiungere Spring AOP nel nostro progetto:

 org.springframework spring-aspects 5.2.8.RELEASE 

Dai un'occhiata a Maven Central per le ultime versioni delle dipendenze Spring-Retry e Spring-Aspects.

3. Abilitazione di Spring Retry

Per abilitare Spring Retry in un'applicazione, dobbiamo aggiungere l' annotazione @EnableRetry alla nostra classe @Configuration :

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

4. Utilizzo di Spring Retry

4.1. @Retryable senza recupero

Per aggiungere la funzionalità di ripetizione dei tentativi ai metodi, possiamo utilizzare l' annotazione @Retryable :

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

In questo esempio, viene eseguito un nuovo tentativo quando viene generata un'eccezione RuntimeException .

Secondo il comportamento predefinito di @Retryable , il nuovo tentativo può essere ripetuto fino a tre volte, con un ritardo di un secondo tra i tentativi .

4.2. @Retryable e @Recover

Aggiungiamo ora un metodo di ripristino utilizzando l' annotazione @Recover :

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

In questo esempio, viene eseguito un nuovo tentativo quando viene generata una SQLException . L' annotazione @Recover definisce un metodo di ripristino separato quando un metodo @Retryable fallisce con un'eccezione specificata.

Di conseguenza, se il metodo retryServiceWithRecovery continua a lanciare una SqlException dopo 3 tentativi, verrà chiamato il metodo recover () .

Il gestore di ripristino dovrebbe avere il primo parametro di tipo Throwable (facoltativo) e lo stesso tipo restituito.I seguenti argomenti vengono popolati dall'elenco degli argomenti del metodo non riuscito nello stesso ordine.

4.3. Personalizzazione del comportamento di @ Retryable

Per personalizzare il comportamento di un nuovo tentativo, possiamo utilizzare i parametri maxAttempts e backoff :

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

Nell'esempio sopra, ci saranno fino a 2 tentativi e un ritardo di 100 millisecondi.

4.4. Utilizzo delle proprietà della molla

Possiamo anche usare le proprietà nell'annotazione @Retryable .

Per dimostrarlo, vedremo come esternalizzare i valori di delay e maxAttempts in un file delle proprietà .

Per prima cosa, definiamo le proprietà in un file chiamato retryConfig. proprietà :

retry.maxAttempts=2 retry.maxDelay=100

Quindi istruiamo la nostra classe @Configuration per caricare questo file:

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

Infine, siamo in grado di inserire i valori di retry.maxAttempts e retry.maxDelay nella nostra definizione @Retryable :

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

Tieni presente che ora stiamo utilizzando maxAttemptsExpression e delayExpression invece di maxAttempts e delay .

5. RetryTemplate

5.1 Riprova

Spring Retry fornisce l' interfaccia RetryOperations che fornisce una serie di metodi execute () :

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

Il RetryCallback che è un parametro di execute () è un'interfaccia che consente l'inserimento della logica di business che deve essere ritentata in caso di errore:

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

5.2. Configurazione RetryTemplate

Il RetryTemplate è un'implementazione delle RetryOperations . Configuriamo un bean RetryTemplate nella nostra 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; } } 

Il RetryPolicy determina quando un'operazione deve essere ripetuta.

Un SimpleRetryPolicy viene utilizzato per riprovare un numero fisso di volte. D'altra parte, BackOffPolicy viene utilizzato per controllare il backoff tra i tentativi.

Infine, una FixedBackOffPolicy si ferma per un periodo di tempo fisso prima di continuare.

5.3. Utilizzando il RetryTemplate

Per eseguire il codice con la gestione dei tentativi possiamo chiamare il metodo r etryTemplate.execute () :

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

Invece di una classe anonima, possiamo usare un'espressione lambda come segue:

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

6. Ascoltatori

I listener forniscono richiamate aggiuntive in caso di tentativi. Possiamo usarli per vari problemi trasversali in diversi tentativi.

6.1. Aggiunta di richiamate

I callback sono forniti in un'interfaccia 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); } }

I callback di apertura e chiusura vengono prima e dopo l'intero tentativo, mentre onError si applica alle singole chiamate RetryCallback .

6.2. Registrazione dell'ascoltatore

Successivamente, registriamo il nostro listener ( DefaultListenerSupport) nel nostro bean RetryTemplate :

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

7. Verifica dei risultati

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.