Spring 重试模板不适用于@org.springframework.transaction.annotation.Transactional

Spring retry template is not working with @org.springframework.transaction.annotation.Transactional

在数据库不可用的情况下,我需要重试执行我的服务方法3次。为此,我使用 Spring 重试模板。但是使用@Transactional 注释,当数据库不可用时,无法捕获特定异常。

@Configuration
public class RetryTemplateConfig {

    @Bean
    public  RetryTemplate createRetryTemplate(){
        RetryTemplate retryTemplate = new RetryTemplate();

        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(5000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(5);
        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }

}


@Service
public class MessageService {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    MessageRepo messageRepo;

    @Transactional
    public void sendMessage(Message message) throws RuntimeException{

        retryTemplate.execute(retryContext -> {
            log.info("Executing for {}  " ,  retryContext.getRetryCount());
            Message model = new Message();
            messageRepo.save(model);
            return "";
        });
    }
}

但是如果我在数据库不可用时尝试使用@Retryable,则 RuntimeExcption 的重试会成功,并在重试结束时调用@Recover 方法。

@Service
@EnableRetry
public class MessageService {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    MessageRepo messageRepo;

    @Retryable(
            value = {RuntimeException.class},
            backoff = @Backoff(delay = 2000),
            maxAttempts = 5
    )
    @Transactional
    public void sendMessage(Message message) throws RuntimeException{
        Message model = new Message();
        messageRepo.save(model);
    }
}


    @Recover
    public void recover(){
        log.info("Recover is called ");
    }
}

有人可以解释这两种不同行为的原因并建议我重试的最佳方法吗? (我正在使用 JPA 数据)

在第一种情况下,在使用 retryTemplate 时,甚至在执行到达 retryTemplate.execute(...) 调用之前就会发生异常,因为 spring 无法打开您需要的事务( 通过 @Transactional 注释,因为打开事务需要数据库连接 ),

因此异常发生在 spring 框架级别,而不是在您的方法中,因此您的重试模板在这种情况下无用。

在第二种情况下,打开事务发生在重试范围内,因此重试捕获异常并且一切正常。