Как использовать CompletableFuture.thenCompose () при возврате сущностей из репозиториев? - PullRequest
0 голосов
/ 30 апреля 2018

Я начал работать с 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 Ответ

0 голосов
/ 30 апреля 2018

Согласно Spring @Async Javadoc :

тип возвращаемого значения ограничен либо void, либо Future

, а также более подробно описано в справочной документации :

В простейшем случае аннотацию можно применить к void методу возврата.

[...]

Даже методы, которые возвращают значение, могут вызываться асинхронно. Однако такие методы должны иметь возвращаемое значение Future. Это все еще обеспечивает преимущество асинхронного выполнения, так что вызывающая сторона может выполнять другие задачи перед вызовом get() в этом Future.

Во втором примере ваши @Async -аннотированные методы не возвращают Future (или ListenableFuture и CompletableFuture, которые также поддерживаются). Однако Spring должен запускать ваш метод асинхронно. Таким образом, он может вести себя так, как будто ваш метод имеет тип возврата void, и поэтому возвращает null.

В качестве примечания: когда вы используете @Async, ваш метод уже будет работать асинхронно, поэтому вы не должны использовать CompletableFuture.supplyAsync() внутри метода. Вы должны просто вычислить свой результат и вернуть его, завернутый в CompletableFuture.completedFuture(), если необходимо. Если ваш метод создает только фьючерсы (например, ваш сервис, который просто создает результаты асинхронного репозитория), то вам, вероятно, не нужна аннотация @Async. См. Также пример из руководства по началу работы .

...