Как использовать 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 аннотаций. Смотрите также пример из руководства по началу работы.

Другие вопросы по тегам