使用 Spring-Retry 的 Kotlin 未调用 @Recover

Posted

技术标签:

【中文标题】使用 Spring-Retry 的 Kotlin 未调用 @Recover【英文标题】:Kotlin with Spring-Retry the @Recover not called 【发布时间】:2019-01-18 03:42:38 【问题描述】:

我在使用 spring boot 和 spring retry 时遇到问题,在进行所有可能的尝试时没有调用 @Recover 注释方法。

我在 kotlin 中使用 spring。

我的应用程序 servlet 容器:

class ServletInitializer : SpringBootServletInitializer() 

    override fun configure(application: SpringApplicationBuilder) : SpringApplicationBuilder 
        return application.sources(SecurityServicesApplication::class.java)
    

我的配置

导入 org.springframework.context.annotation.Configuration 导入 org.springframework.retry.annotation.EnableRetry

@Configuration
@EnableRetry
class RetryConfig

更新

我的服务

import security.commons.exception.SecurityException
import org.apache.commons.lang3.exception.ExceptionUtils
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.retry.annotation.Backoff
import org.springframework.retry.annotation.Recover
import org.springframework.retry.annotation.Retryable
import org.springframework.security.oauth2.client.OAuth2RestTemplate
import org.springframework.security.oauth2.common.OAuth2AccessToken
import org.springframework.stereotype.Service
import java.net.ConnectException

@Service
class AuthorizationServerTokenRequester 

    val log = LoggerFactory.getLogger(Oauth2Service::class.java)!!

    @Value("\$accessTokenUri")
    private val accessTokenUri: String? = null


    @Retryable(
            value = [SecurityException::class],
            maxAttemptsExpression = "\$server.oauthclient.retry.maxAttempts",
            backoff = Backoff(delayExpression = "\$server.oauthclient.retry.delay"))
    fun token(oauth2RestTemplate: OAuth2RestTemplate): OAuth2AccessToken? 
        try 

            return oauth2RestTemplate.accessToken
         catch (ex: Exception) 
            if (ExceptionUtils.getRootCause(ex) is ConnectException) 
                log.error("trying again....")
            
            throw com.security.commons.exception.SecurityException("")
        
    

    @Recover
    fun recover(ex: SecurityException) 
        print("##############################################################################################sssss# SecurityException")
    

我的错误日志:

2018-08-10 11:28:41.802 DEBUG 40168 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : 
    Written [timestamp=Fri Aug 10 11:28:41 BRT 2018, status=500, error=Internal Server Error, exception=org.springframework.retry.ExhaustedRetryException, 
              message=Cannot locate recovery method; nested exception is security.commons.exception.SecurityException: , 
              path=/security/api/v1/oauth2/token] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@21e2d8f6]

解决方案 [在 @Gary Russell 的帮助下]

@Recover 方法的返回必须与@Retryable 方法相同

fun recover(ex: S@RecoverecurityException, oauth2RestTemplate: OAuth2RestTemplate ) : OAuth2AccessToken 
    print("##############################################################################################sssss# SecurityException")
    throw br.com.totvs.security.commons.exception.SecurityException("")

【问题讨论】:

我认为@Retryable应该用在接口而不是类中 @lukaszrys 从文档中的github.com/spring-projects/spring-retry 类中,可重试工作正常,恢复未被调用 请看看这个答案***.com/a/46731031/5289288 【参考方案1】:

@Recover 方法的返回类型必须与@Retryable 方法相同。

编辑

这对我来说很好......

@SpringBootApplication
@EnableRetry
public class So51787951Application 

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

    @Bean
    public ApplicationRunner runner(Foo foo) 
        return args -> 
            try 
                foo.foo("bar");
            
            catch (Exception e) 

            
        ;
    

    @Bean
    public Foo foo() 
        return new Foo();
    


open class Foo 

    @Retryable(maxAttempts = 3)
    open fun foo(data: String) :String? 
        println(data)
        throw Exception("foo")
    

    @Recover
    open fun rec(data: Exception) :String? 
        println("Recovered")
        return null;
    


bar
bar
bar
Recovered

请注意,我必须创建 Foo 及其方法 open,因为 Spring 创建了一个 CGLIB 代理子类,因为没有接口。

【讨论】:

没用,我通过界面试了也没用,我加了错误信息,提示没有找到recover方法 对不起,我没有阅读“返回类型”,据我了解,我最终阅读了参数,我只是调试了RecoverAnnotationRecoveryHandler类,实际上返回必须等于“如果(恢复! = null && method.getReturnType().isAssignableFrom(failingMethod.getReturnType ()))”,感谢大家的关注和支持 @Gary Russell 我们还需要在我们的 main 中将“foo”的调用包装在 try/catch 块中吗?我认为可重试符号在它自己的实现中为我们包装了它,“Recover”方法是 catch 块

以上是关于使用 Spring-Retry 的 Kotlin 未调用 @Recover的主要内容,如果未能解决你的问题,请参考以下文章

spring-retry失败重试

spring-retry失败重试

spring-retry重试机制使用

Spring-Retry

Spring-Retry 没有从 HikariCP 获得新的数据库连接

Spring-Retry 和 Guava-Retry