1. 概要

Spring Retryは、失敗した操作を自動的に再起動する機能を提供します。 これは、エラーが一時的なものである可能性がある場合(瞬間的なネットワークグリッチなど)に役立ちます。

In this tutorial, we’ll see the various ways to use Spring Retry: annotations, RetryTemplate, and callbacks.

2. Mavenの依存関係

から始めましょうspring-依存関係をpom.xmlファイルに再試行します。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

また、SpringAOPをプロジェクトに追加する必要があります。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

spring-retryおよびspring-aspectsの依存関係の最新バージョンについては、MavenCentralをご覧ください。

3. 春の再試行を有効にする

アプリケーションでSpringRetryを有効にするには、@EnableRetryアノテーション@Configurationクラスに追加する必要があります。

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

4. SpringRetryの使用

4.1. @Retryableリカバリなし

@Retryableアノテーションを使用して、メソッドに再試行機能を追加できます

@Service
public interface MyService { 

    @Retryable(value = RuntimeException.class) 
    void retryService(String sql); 
}

ここでは、RuntimeExceptionがスローされたときに再試行が試行されます

@Retryable のデフォルトの動作により、再試行は最大3回発生する可能性があり、再試行の間に1秒の遅延があります。

4.2. @Retryableおよび @回復

@Recoverアノテーションを使用してリカバリメソッドを追加しましょう

@Service
public interface MyService { 

    @Retryable(value = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException; 

    @Recover
    void recover(SQLException e, String sql); 
}

ここでは、SQLExceptionがスローされたときに再試行が試行されます@Recoverアノテーションは、@Retryableメソッドが指定された例外で失敗したときに別のリカバリメソッドを定義します。

したがって、 retryServiceWithRecovery メソッドが3回試行した後も、 SqlException をスローし続けると、 restore()メソッドが呼び出されます。

リカバリー・ハンドラーは、タイプ Throwable (オプション)の最初のパラメーターと同じ戻りタイプを持っている必要があります。以下 引数は、失敗したメソッドの引数リストから同じ順序で入力されます。

4.3. @Retryableの動作のカスタマイズ

再試行の動作をカスタマイズするために、パラメーターmaxAttemptsおよび backoffを使用できます。

@Service
public interface MyService {

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

最大2回の試行と100ミリ秒の遅延があります。

4.4. Springプロパティの使用

@Retryableアノテーションでプロパティを使用することもできます。

これを実証するために、delayとmaxAttemptsの値をプロパティファイルに外部化する方法を見ていきます。

まず、retryConfig。propertiesというファイルでプロパティを定義しましょう。

retry.maxAttempts=2
retry.maxDelay=100

次に、@Configurationクラスにこのファイルをロードするように指示します。

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

最後に、@Retryable定義にretry.maxAttemptsとretry.maxDelayの値を挿入できます。

@Service 
public interface MyService {

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

現在、maxAttemptsおよびdelayの代わりに、maxAttemptsExpressionおよびdelayExpressionを使用していることに注意してください。

5. RetryTemplate

5.1. RetryOperations

Spring Retryは、 RetryOperations インターフェイスを提供します。これは、 execute()メソッドのセットを提供します。

public interface RetryOperations {

    <T> T execute(RetryCallback<T> retryCallback) throws Exception;

    ...
}

The RetryCallback, which is a parameter of the execute(), is an interface that allows the insertion of business logic that needs to be retried upon failure:

public interface RetryCallback<T> {

    T doWithRetry(RetryContext context) throws Throwable;
}

5.2. RetryTemplate構成

RetryTemplate は、RetryOperationsの実装です。

@ConfigurationクラスでRetryTemplatebeanを構成しましょう。

@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;
    }
}

RetryPolicy は、操作を再試行するタイミングを決定します。

SimpleRetryPolicy は、一定の回数再試行するために使用されます。 一方、 BackOffPolicy は、再試行間のバックオフを制御するために使用されます。

最後に、 FixedBackOffPolicy は、続行する前に一定期間一時停止します。

5.3. RetryTemplateを使用する

再試行処理を使用してコードを実行するには、 r etryTemplate.execute()メソッドを呼び出すことができます。

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

匿名クラスの代わりに、ラムダ式を使用できます:

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

6. リスナー

リスナーは、再試行時に追加のコールバックを提供します。 そして、これらをさまざまな再試行にわたるさまざまな横断的関心事に使用できます。

6.1. コールバックの追加

コールバックはRetryListenerインターフェースで提供されます。

public class DefaultListenerSupport extends RetryListenerSupport {
    
    @Override
    public <T, E extends Throwable> void close(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onClose");
        ...
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onError"); 
        ...
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context,
      RetryCallback<T, E> callback) {
        logger.info("onOpen");
        ...
        return super.open(context, callback);
    }
}

openおよびcloseコールバックは、再試行全体の前後に発生しますが、onErrorは個々のRetryCallback呼び出しに適用されます。

6.2. リスナーの登録

次に、リスナー( DefaultListenerSupport)RetryTemplateBeanに登録します。

@Configuration
public class AppConfig {
    ...

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

7. 結果のテスト

例を終了するために、結果を確認しましょう。

@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;
        });
    }
}

テストログからわかるように、RetryTemplateRetryListenerを適切に構成しました。

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. 結論

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

例のソースコードは、GitHubから入手できます。