为什么我的回调方法不能正常工作?

时间:2016-07-16 09:02:27

标签: java asynchronous callback resttemplate spring-web

我使用AsyncRestTemplate异步进行resttemplate。

这些方法应该等待所有asyncresttemplate进程直到完成,并且它将返回reviewContent。

问题是回调方法不起作用,在整个方法完成之前。因此,我无法获取optionName的正确返回值,并且membership VintageCodeCode和reviewType应包含在reviewContent中。

有人可以解释我现在缺少什么吗?

转#1 成功回调方法会改变reviewContent的状态,这可能是个问题吗?

public ReviewContent getRepresentativeReviewContent(Long dealNo, Long categoryNo, String setId) {

    Optional<Map<String, Object>> review = Optional.ofNullable(boardApi.getRepresentativeReviewContent(dealNo));

    if (review.isPresent()) {
        Long memberNo = Long.valueOf(review.get().get("memberNo").toString());
        ReviewContent reviewContent  = new ReviewContent();

        ListenableFuture<ResponseEntity<Map>> optionInfo = dealApi.asyncGetDealOption(Long.valueOf(review.get().get("optionNo").toString()));
        optionInfo.addCallback(success -> {
            try {
                reviewContent.setOptionName((String) ((Map<String, Object>) success.getBody().get("data")).get("dealTitle"));
            } catch (Exception e) {
                reviewContent.setOptionName(null);
            }
        }, failure -> LOGGER.error("asyncGetDealOption", failure.getStackTrace()));

        ListenableFuture<ResponseEntity<Map>> gradeInfoOfThisMember = mktApi.asyncGetMembershipGradeOfThisMember(memberNo);
        gradeInfoOfThisMember.addCallback(success -> {
                    try {
                        reviewContent.setMembershipGradeCode((Integer) ((Map<String, Object>) success.getBody().get("data")).get("grade"));
                    } catch (Exception e) {
                        reviewContent.setMembershipGradeCode(0);
                    }
                        },
                        failure -> {
                            reviewContent.setMembershipGradeCode(0);
                            LOGGER.error("asyncGetMembershipGradeOfThisMember", failure.getStackTrace());
                        });

        ListenableFuture<ResponseEntity<ReviewType>> reviewTypeByCategoryNo = boardApi.asyncGetReviewTypeByCategoryNo(categoryNo, setId);
        reviewTypeByCategoryNo.addCallback(success -> {
                    try {
                        reviewContent.setReviewType(success.getBody());
                    } catch (Exception e) {
                        reviewContent.setReviewType(null);
                    }
                },
                failure -> {
                    reviewContent.setReviewType(null);
                    LOGGER.error("asyncGetReviewTypeByCategoryNo", failure.getStackTrace());
                });

        reviewContent.setReviewCount((Integer) review.get().get("reviewCount"));
        reviewContent.setReviewAvgScore((Double) review.get().get("reviewAvgScore"));
        reviewContent.setContents((String) review.get().get("contents"));
        reviewContent.setCreateDt((String) review.get().get("createdDt"));
        reviewContent.setUpdateDt((String) review.get().get("updatedDt"));
        reviewContent.setMemberSrl(memberNo);
        reviewContent.setTitle((String) review.get().get("title"));
        reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));
        reviewContent.setMemberId((String) review.get().get("memberId"));
        reviewContent.setAccountSrl(Long.valueOf(review.get().get("accountNo").toString()));

        boolean isApiExecutionDone = false;
        while (!isApiExecutionDone) {
            if (gradeInfoOfThisMember.isDone() && optionInfo.isDone() && reviewTypeByCategoryNo.isDone()) {
                isApiExecutionDone = true;
            }
        }

        return reviewContent;
    }

    return new ReviewContent();
}

1 个答案:

答案 0 :(得分:0)

所以你的问题是回调设置了方法返回的对象的属性。但是,它们异步执行,并且不属于done的{​​{1}}状态:它们在Future完成后自行执行,同时执行使用Future方法中的代码。由于该方法在完成所有getRepresentativeReviewContent后立即返回,因此属性不会(全部)按原样设置。

此外,您没有显示Future对象的代码,但我很确定它没有声明ReviewContentoptionTypemembershipGradeCode字段为reviewType。由于方法中没有障碍(例如volatile块或synchronized s),因此无法保证Java内存模型中可以看到回调中设置的值(即在其他线程中)在执行Lock方法的线程中。

回调只应用于主执行路径之外的副作用,因为它很难与它们协调:你必须使用像getRepresentativeReviewContent这样的东西来确保它们全部被执行,那就是使代码更复杂。

只需等待异步结果(但代码未经测试):

CountDownLatch

另一种选择是组合try { // Not sure why you need to catch Exception here? // If it's for flow control (absent entry in a Map), it's a bad practice. // Just check it instead of catching NullPointerException. reviewContent.setOptionName((String) ((Map<String, Object>) optionInfo.get().getBody().get("data")) .get("dealTitle")); } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOGGER.error("asyncGetDealOption", e); reviewContent.setOptionName(null); } catch (CancellationException | ExecutionException e) { LOGGER.error("asyncGetDealOption", e); reviewContent.setOptionName(null); } ,例如可以使用Guava的Futures.transform来完成从完整响应中获取所需的字符串,这样你就可以调用{{1}在那个组成Future来设置你的属性。但是,您仍然需要管理错误。