为什么我的回调方法不能正常工作?
Why aren't my callback methods working properly?
我使用 AsyncRestTemplate 异步制作 resttemplate。
这些方法应该等待所有 asyncresttemplate 进程完成,并且它将 return reviewContent。
问题是在整个方法工作完成之前,回调方法不起作用。所以我不能采用正确的 return optionName 和 membershipGradeCode 的值,并且 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();
}
所以你的问题是回调在你的方法返回的对象上设置了属性。但是,它们 也是 异步执行的,并且不是 Future
的 done
状态的一部分:一旦 Future
是,它们自己就会执行与 getRepresentativeReviewContent
方法中的代码同时完成。由于方法 returns 一旦所有 Future
完成,属性就不会(全部)设置为它们应该的。
此外,您没有显示 ReviewContent
对象的代码,但我很确定它没有声明 optionType
、membershipGradeCode
或 reviewType
字段为 volatile
。由于方法中没有障碍(例如 synchronized
块或 Lock
s),因此无法保证 Java 内存模型中回调中设置的值(即在其他线程中) ) 将在执行 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);
}
另一种选择是组合 Future
s,例如可以使用 Guava 的 Futures.transform
来实际从完整响应中获取您需要的字符串,因此您可以调用 get()
在 Future
上设置你的 属性。不过,您仍然需要管理错误。
我使用 AsyncRestTemplate 异步制作 resttemplate。
这些方法应该等待所有 asyncresttemplate 进程完成,并且它将 return reviewContent。
问题是在整个方法工作完成之前,回调方法不起作用。所以我不能采用正确的 return optionName 和 membershipGradeCode 的值,并且 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();
}
所以你的问题是回调在你的方法返回的对象上设置了属性。但是,它们 也是 异步执行的,并且不是 Future
的 done
状态的一部分:一旦 Future
是,它们自己就会执行与 getRepresentativeReviewContent
方法中的代码同时完成。由于方法 returns 一旦所有 Future
完成,属性就不会(全部)设置为它们应该的。
此外,您没有显示 ReviewContent
对象的代码,但我很确定它没有声明 optionType
、membershipGradeCode
或 reviewType
字段为 volatile
。由于方法中没有障碍(例如 synchronized
块或 Lock
s),因此无法保证 Java 内存模型中回调中设置的值(即在其他线程中) ) 将在执行 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);
}
另一种选择是组合 Future
s,例如可以使用 Guava 的 Futures.transform
来实际从完整响应中获取您需要的字符串,因此您可以调用 get()
在 Future
上设置你的 属性。不过,您仍然需要管理错误。