Spring Boot跨服务调用GetDataset接口失败排查求助
问题背景
我是Spring Boot新手,正在实现两个服务间的通信。两个服务单独运行正常,但服务间调用GetDataset接口时出错,Check接口则能正常工作。Server2的GetDataset接口通过Postman调用可正常返回嵌套HashMap,但通过Server1调用时出现反序列化错误。
相关代码
Server1代码
IndexController
@RestController @AllArgsConstructor @RequestMapping(path = "/webpage") public class IndexController { @Autowired private CheckService checkService; @Autowired private SearchService searchService; @Autowired private GetDatasetService getDatasetService; @GetMapping(path = {"check"}) public String CheckDataset(@RequestParam List<String> checking) { return checkService.checkDataset(checking); } @GetMapping(path = {"GetDataset"}) public HashMap<String, HashMap<String, List<String>>> GetDataset(@RequestParam List<String> userinfo) { return getDatasetService.getDataset(userinfo); } }
异常的GetDatasetService
public class GetDatasetService { @Value("${first.year.project.url}") private String url; public HashMap<String, HashMap<String, List<String>>> getDataset(List<String> userinfo){ String urlForGetDataset = url + "/api/GetDataset"; URI uri = UriComponentsBuilder .fromHttpUrl(urlForGetDataset) .queryParam("userinfo",userinfo) .build(Collections.emptyMap()); return WebClient.create() .get() .uri(uri) .retrieve() .bodyToMono(GetDatasetModel.class) .block(); } }
正常工作的CheckService
@Component public class CheckService { @Value("${first.year.project.url}") private String url; public String checkDataset(List<String> list){ String urlForrCheck = url + "/api/Check"; URI uri = UriComponentsBuilder .fromHttpUrl(urlForrCheck) .queryParam("checking",list) .build(Collections.emptyMap()); String check = WebClient.create() .get() .uri(uri) .retrieve() .bodyToMono(String.class) .block(); return check; } }
Server2代码
RequestsController
@AllArgsConstructor @RestController @RequestMapping(path = {"/api"}) public class RequestsController { private final HashMap<String, HashMap<String,String>> DatasetMaps = ProcessingApplication.DatasetMaps; @GetMapping(path = {"Check"}) public String Check(@RequestParam List<String> checking){ GetCheck getCheck = new GetCheck(this.DatasetMaps); CheckModel checkModel = new CheckModel(); checkModel.setList(checking); return getCheck.GetCheckDatasetMethod(checkModel.checking); } @GetMapping(path = {"GetDataset"}) public HashMap<String, HashMap<String, List<String>>> GetDataset(@RequestParam List<String> userinfo) throws IOException { GetDatasetModel getDatasetModel = new GetDatasetModel(); getDatasetModel.setList(userinfo); GetDataset getDataset = new GetDataset(); return getDataset.GetDatasetMethod(getDatasetModel.userinfo.get(0),getDatasetModel.userinfo.get(1)); } }
CheckModel类
@Getter @Setter public class CheckModel { protected List<String> checking; public CheckModel(){} public void setList(List<String> list){ this.checking = list; } }
GetDatasetModel类
@Setter @Getter public class GetDatasetModel { protected List<String> userinfo; public GetDatasetModel(){} public void setList(List<String> userinfo){ this.userinfo=userinfo; } }
错误栈信息
org.springframework.core.codec.CodecException: Type definition error: [map type; class com.Portal.Indexes.HTMLModel.GetDatasetModel, [simple type, class java.lang.String] -> [map type; class java.util.HashMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]] at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:269) Suppressed: The stacktrace has been enhanced by Reactor, refer to additional information below: Error has been observed at the following site(s): *__checkpoint ⇢ Body from GET http://127.0.0.1:1200/api/GetDataset?userinfo=Alex&userinfo=check [DefaultClientResponse] Original Stack Trace: at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:269) at org.springframework.http.codec.json.AbstractJackson2Decoder.decode(AbstractJackson2Decoder.java:209) at org.springframework.http.codec.json.AbstractJackson2Decoder.lambda$decodeToMono$2(AbstractJackson2Decoder.java:189) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132) at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299) at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071) at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145) at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:415) at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:431) at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:485) at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:712) at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.
解决方案
问题根源
GetDatasetService中用bodyToMono(GetDatasetModel.class)尝试把Server2返回的嵌套HashMap转换成GetDatasetModel对象,但Server2实际返回的是HashMap<String, HashMap<String, List<String>>>类型,和GetDatasetModel的结构完全不匹配,导致Jackson反序列化失败。另外,GetDatasetService缺少Spring管理注解,会导致注入失败。
修复步骤
修正WebClient的反序列化类型
直接让WebClient接收Server2返回的嵌套HashMap类型,使用ParameterizedTypeReference指定泛型结构:@Service public class GetDatasetService { @Value("${first.year.project.url}") private String url; public HashMap<String, HashMap<String, List<String>>> getDataset(List<String> userinfo){ String urlForGetDataset = url + "/api/GetDataset"; URI uri = UriComponentsBuilder .fromHttpUrl(urlForGetDataset) .queryParam("userinfo",userinfo) .build(Collections.emptyMap()); return WebClient.create() .get() .uri(uri) .retrieve() .bodyToMono(new ParameterizedTypeReference<HashMap<String, HashMap<String, List<String>>>>() {}) .block(); } }添加Spring管理注解
给GetDatasetService添加@Service或@Component注解,确保Spring能自动注入该类。可选:统一返回载体(推荐)
如果想使用GetDatasetModel作为返回载体,需要修改Server2的接口,让它返回包含HashMap的GetDatasetModel对象:- 修改Server2的
RequestsController:@GetMapping(path = {"GetDataset"}) public GetDatasetModel GetDataset(@RequestParam List<String> userinfo) throws IOException { GetDatasetModel getDatasetModel = new GetDatasetModel(); getDatasetModel.setList(userinfo); GetDataset getDataset = new GetDataset(); HashMap<String, HashMap<String, List<String>>> result = getDataset.GetDatasetMethod(getDatasetModel.userinfo.get(0),getDatasetModel.userinfo.get(1)); getDatasetModel.setDataset(result); return getDatasetModel; } - 修改
GetDatasetModel类,添加存储结果的字段:@Setter @Getter public class GetDatasetModel { protected List<String> userinfo; private HashMap<String, HashMap<String, List<String>>> dataset; public GetDatasetModel(){} public void setList(List<String> userinfo){ this.userinfo=userinfo; } }
此时Server1的
bodyToMono(GetDatasetModel.class)就能正常反序列化,后续从model中取出dataset即可。- 修改Server2的
内容的提问来源于stack exchange,提问作者ilie alexandru

