Как использовать CompletableFuture.thenCompose() при возврате сущностей из репозиториев?
Я начал работать с CompletableFuture
в Spring Boot, и в некоторых местах я вижу, что обычные методы репозитория возвращают CompletableFuture <Entity>
вместо Entity
,
Я не знаю, что происходит, но когда я возвращаю экземпляры CompletableFuture
в репозиториях код работает отлично. Однако, когда я возвращаю сущности, код не работает асинхронно и всегда возвращает null
,
Вот пример:
@Service
public class AsyncServiceImpl{
/** .. Init repository instances .. **/
@Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public CompletableFuture<Token> getTokenByUser(Credential credential) {
return userRepository.getUser(credential)
.thenCompose(s -> TokenRepository.getToken(s));
}
}
@Repository
public class UserRepository {
@Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public CompletableFuture<User> getUser(Credential credentials) {
return CompletableFuture.supplyAsync(() ->
new User(credentials.getUsername())
);
}
}
@Repository
public class TokenRepository {
@Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public CompletableFuture<Token> getToken(User user) {
return CompletableFuture.supplyAsync(() ->
new Token(user.getUserId())
);
}
}
Предыдущий код работает отлично, но следующий код не работает асинхронно, и результат всегда null
,
@Service
public class AsyncServiceImpl {
/** .. Init repository instances .. **/
@Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public CompletableFuture<Token> requestToken(Credential credential) {
return CompletableFuture.supplyAsync(() -> userRepository.getUser(credential))
.thenCompose(s ->
CompletableFuture.supplyAsync(() -> TokenRepository.getToken(s)));
}
}
@Repository
public class UserRepository {
@Async(AsyncConfiguration.TASK_EXECUTOR_REPOSITORY)
public User getUser(Credential credentials) {
return new User(credentials.getUsername());
}
}
@Repository
public class TokenRepository {
@Async(AsyncConfiguration.TASK_EXECUTOR_SERVICE)
public Token getToken(User user) {
return new Token(user.getUserId());
}
}
Почему этот второй код не работает?
1 ответ
Согласно Spring @Async Javadoc:
тип возврата ограничен либо
void
или жеFuture
и это также подробно описано в справочной документации:
В простейшем случае аннотация может применяться к
void
возвратный метод.[...]
Даже методы, которые возвращают значение, могут быть вызваны асинхронно. Однако такие методы должны иметь
Future
возвращаемое значение Это все еще обеспечивает преимущество асинхронного выполнения, так что вызывающая сторона может выполнять другие задачи перед вызовомget()
на это будущее.
Во втором примере ваш @Async
Аннотированные методы не возвращают Future
(или же ListenableFuture
а также CompletableFuture
которые также поддерживаются). Однако Spring должен запускать ваш метод асинхронно. Таким образом, он может вести себя так, как если бы ваш метод имел void
тип возврата, и, следовательно, он возвращает null
,
Как примечание стороны, когда вы используете @Async
ваш метод уже будет работать асинхронно, поэтому вы не должны использовать CompletableFuture.supplyAsync()
внутри метода. Вы должны просто вычислить свой результат и вернуть его, завернутый в CompletableFuture.completedFuture()
если необходимо. Если ваш метод создает только фьючерсы (например, ваш сервис, который просто создает результаты асинхронного хранилища), то вам, вероятно, не нужен @Async
аннотаций. Смотрите также пример из руководства по началу работы.