使用Kotlin的异步Spring Boot无法正常工作

时间:2015-12-21 11:48:51

标签: spring asynchronous callback spring-boot kotlin

我正在尝试创建一个异步执行操作的Spring Service并返回router.post('/login'...。我希望在操作失败时触发失败回调 - 我尝试这样做是为了使用ListenableFuture,如下所示:

AsyncResult.forExecutionException

切入点:

@Service
open class UserClientService {

    @Async
    fun fetchUser(email: String): ListenableFuture<User> {
        val uri = buildUri(email)
        val headers = buildHeaders()
        try {
            val result = restTemplate.exchange(uri, HttpMethod.GET, HttpEntity<Any>(headers), User::class.java)
            return AsyncResult.forValue(result.body)
        } catch (e: RestClientException) {
            return AsyncResult.forExecutionException(e)
        }
    }
}

Spring RestController实现如下:

@SpringBootApplication
@EnableAsync
open class UserProxyApplication

fun main(args: Array<String>) {
    SpringApplication.run(UserProxyApplication::class.java, *args)
} 

问题是在@RestController @RequestMapping("/users") class UserController @Autowired constructor( val client: UserClientService ) { @RequestMapping(method = arrayOf(RequestMethod.GET)) fun getUser(@RequestParam(value = "email") email: String): DeferredResult<ResponseEntity<User>> { val result = DeferredResult<ResponseEntity<User>>(TimeUnit.SECONDS.toMillis(10)) client.fetchUser(email).addCallback( { success -> result.setResult(ResponseEntity.ok(success)) }, { failure -> result.setResult(ResponseEntity(HttpStatus.NOT_FOUND)) } ) return result; } } REST调用中抛出异常时,永远不会触发UserController中的失败回调。相反,成功回调是在UserClientService参数为success的情况下触发的。

在Kotlin中,我可以使用null检查成功是否为空 - 这会引发异常,然后 触发故障回调,其中success!!参数为NPE。

问题是如何在failure中发生异常时触发UserController中的失败回调?

更新A 似乎所有内容都在同一个线程“http-nio-8080-exec-XXX”上执行,无论我是否使用UserClientService - 请参阅注释。

1 个答案:

答案 0 :(得分:1)

这一切都适用于:

A)方法fetchUser被声明为open,即不是最终方法,以便Spring可以代理呼叫

...或...

B)您创建了一个接口IUserClientService并在UserController的构造函数中使用它:

interface IUserClientService {
    fun fetchUser(email: String): ListenableFuture<User>
}

现在UserClientService实现了接口:

@Service
open class UserClientService : IUserClientService {

    @Async
    override fun fetchUser(email: String): ListenableFuture<User> {
// ... rest as shown in question ...

最后是UserController

@RestController
@RequestMapping("/users")
class UserController @Autowired constructor(
    val client: IUserClientService
) {
    @RequestMapping(method = arrayOf(RequestMethod.GET))
    fun getUser(@RequestParam(value = "email") email: String): DeferredResult<ResponseEntity<User>> {
// ... rest as shown in question ...

不确定这是否是因为我使用的是Kotlin。我见过的例子并不需要实现一个界面。