Я пытаюсь создать Spring Service, которая выполняет операцию асинхронно и возвращает ListenableFuture
. Я хочу, чтобы обратный вызов отказа срабатывал, когда операция AsyncResult.forExecutionException
неудачно – моя попытка сделать это заключается в использовании 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) } } }
Пункт входа:
@SpringBootApplication @EnableAsync open class UserProxyApplication fun main(args: Array<String>) { SpringApplication.run(UserProxyApplication::class.java, *args) }
Реализация Spring RestController выглядит следующим образом:
@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; } }
Проблема заключается в том, что обратный вызов сбоя в UserController
никогда не запускается, если в UserClientService
REST UserClientService
. Вместо этого обратный вызов успеха запускается с аргументом success
равным null
.
В Котлине я могу проверить, является ли успех нулевым, используя success!!
– это генерирует исключение, которое затем запускает обратный вызов failure
аргументом failure
являющимся NPE.
Вопрос в том, как я могу вызвать обратный вызов сбоя в UserController
когда в UserClientService
произошло UserClientService
?
Обновление A Кажется, что все выполняется в том же потоке «http-nio-8080-exec-XXX», независимо от того, использую ли я @Async
или нет – см. Комментарии.
Все это работает, если:
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. Примеры, которые я видел, не требуют реализации интерфейса.