引入Azure Service Bus的Spring Cloud Stream Binder致测试失败
问题:引入Azure Stream Binder依赖后RestTemplate测试部分失败
添加以下Maven依赖后:
<dependency> <groupId>com.azure.spring</groupId> <artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId> </dependency>
部分RestTemplate客户端测试突然失败,错误分为两类:
错误1:JSON断言失败
java.lang.AssertionError: JSON path "$" Expected: (a collection containing <152> and a collection containing <153>) but: a collection containing <152> was String "<UnmodifiableRandomAccessList><item>152</item><item>153</item></UnmodifiableRandomAccessList>"
错误2:请求Content-Type不符
Expected :application/json Actual :application/xml;charset=UTF-8
测试代码示例:
@Autowired private RestTemplate restTemplate; private MockRestServiceServer mockServer; @BeforeEach public void init() { mockServer = MockRestServiceServer.createServer(restTemplate); } public void test() throws Exception { mockServer.expect(requestTo(url)) .andExpect(method(HttpMethod.POST)) .andExpect(jsonPath("$", hasItems(152, 153))) .andRespond(withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) .body("{}")); // actual code removed for brevity ResultDTO result = client.makeRestCall(Set.of(1L)); // 内部通过RestTemplate调用目标URL assertNotNull(result); }
奇怪的是测试类中仅4/8个测试失败,无明显规律,移除该Azure依赖后所有测试恢复正常。
原因分析
问题的核心是Azure Stream Binder依赖间接引入了XML消息转换器,打乱了RestTemplate的消息转换优先级:
- 该依赖会自动引入
jackson-dataformat-xml依赖,导致Spring自动注册MappingJackson2XmlHttpMessageConverter到RestTemplate的转换器列表中 - 当RestTemplate没有显式配置转换器顺序时,XML转换器的优先级可能高于JSON转换器
- 这会引发两个问题:
- 发送请求时,原本要序列化为JSON的集合被转换成了XML格式(即错误中的
<UnmodifiableRandomAccessList>标签内容) - 请求的Content-Type被自动设置为
application/xml,与测试预期的application/json不符
- 发送请求时,原本要序列化为JSON的集合被转换成了XML格式(即错误中的
- 部分测试正常的原因是这些测试可能显式指定了请求Content-Type,或者在特定场景下RestTemplate优先匹配到了JSON转换器
解决方法
1. 显式配置RestTemplate的转换器顺序
在配置RestTemplate Bean时,确保JSON转换器排在XML转换器之前:
@Bean public RestTemplate restTemplate(ObjectMapper objectMapper) { RestTemplate restTemplate = new RestTemplate(); List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters(); // 移除已有的XML转换器(如果存在) converters.removeIf(converter -> converter instanceof MappingJackson2XmlHttpMessageConverter); // 将JSON转换器放到最前面,确保优先使用 MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(objectMapper); converters.add(0, jsonConverter); // 若业务需要XML转换,再将XML转换器添加到列表末尾 converters.add(new MappingJackson2XmlHttpMessageConverter()); return restTemplate; }
2. 排除Azure依赖中的XML转换包
在Maven依赖中排除jackson-dataformat-xml,避免自动注册XML转换器:
<dependency> <groupId>com.azure.spring</groupId> <artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </exclusion> </exclusions> </dependency>
3. 在请求中显式指定Content-Type
发送请求时明确设置请求头为application/json,强制RestTemplate使用JSON转换器:
HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<Set<Long>> requestEntity = new HttpEntity<>(Set.of(1L), headers); ResultDTO result = restTemplate.postForObject(url, requestEntity, ResultDTO.class);
内容的提问来源于stack exchange,提问作者Daniel Pop
相关产品推荐
相关产品推荐

