Spring 重试:如何使@Bean 的所有方法都可重试?

Spring Retry: How to make all methods of a @Bean retryable?

我想创建一个 @Bean 第三方服务,如 Keycloak(或任何其他服务),在任何给定时间可能无法访问。该对象应该重试生成的 Keycloak bean 的所有方法。

我尝试了以下方法:

@Configuration
@EnableRetry
class KeycloakBeanProvider() {

    @Bean
    @Retryable
    fun keycloak(oauth2ClientRegistration: ClientRegistration): Keycloak {
        return KeycloakBuilder.builder()
            .serverUrl(serverUrl)
            .realm(oauth2ClientRegistration.clientName)
            .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
            .clientId(oauth2ClientRegistration.clientId)
            .clientSecret(oauth2ClientRegistration.clientSecret)
            .build()
    }
}

但是这样只会重试 bean 创建,而不是对 bean 的实际方法调用。我知道 @Retryable 可以在 class 级别上使用,但我没有 Keycloak class 级别,所以我无法在此处添加它。

如何使生成的 Keycloak bean 的方法可重试?

您必须将 Keycloak 注释为 @Retryable

@SpringBootApplication
@EnableRetry
public class So70593939Application {

    public static void main(String[] args) {
        SpringApplication.run(So70593939Application.class, args);
    }

    @Bean
    ApplicationRunner runner(Foo foo) {
        return args -> {
            foo.foo("called foo");
            foo.bar("called bar");
        };
    }

}

@Component
@Retryable
class Foo {

    public void foo(String in) {
        System.out.println("foo");
        throw new RuntimeException("test");
    }

    public void bar(String in) {
        System.out.println("bar");
        throw new RuntimeException("test");
    }

    @Recover
    public void recover(Exception ex, String in) {
        System.out.println(ex.getMessage() + ":" + in);
    }

}
foo
foo
foo
test:called foo
bar
bar
bar
test:called bar

如果您无法注释 class(例如,因为它来自另一个项目),您需要使用 RetryTemplate 来调用其方法,而不是使用基于注释的重试。

您可以手动检测您的 class。检查 documentation.

@Bean
public Keycloak myService() {
    ProxyFactory factory = new ProxyFactory(RepeatOperations.class.getClassLoader());
    factory.setInterfaces(Keycloak.class);
    factory.setTarget(createKeycloak());

    Keycloak keycloak = (Keycloak) factory.getProxy();
    JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
    pointcut.setPatterns(".*.*");

    RetryOperationsInterceptor interceptor = new RetryOperationsInterceptor();

    ((Advised) keycloak).addAdvisor(new DefaultPointcutAdvisor(pointcut, interceptor));

    return keycloak;
}