一、需求场景:
有个需求场景:同一个请求路径,入参类型分别有“JSON格式”和“XML格式”的入参,需要根据入参类型的不同,处理不同的业务场景,然后返回对应格式的返回。值。比如:
JSON格式入参,返回JSON格式的返回值。
XML格式入参,返回XML格式的返回值。
印象中是可以区分的,然后网上搜索了资料完善了解决方案,好记性不如烂笔头,现在梳理一下如下:
二、解决方案
第一种方案:SpringMvc 自定义 XML的 HttpMessageConverter 转换器。
自定义转换器的方式本身没有问题,自己写一个 继承 ObjectMapper 类的xmlMapper就能实现。一开始我是用的这个,后来发现这样写很多余,因为 ObjectMapper 我是引用了"com.fasterxml.jackson.dataformat.xml.XmlMapper" ,实际上完全可以不用自己写,原因是 :如果引入了"com.fasterxml.jackson.dataformat.xml.XmlMapper"依赖,SpringMVC会自动加载XML转换器,优先级是XML优先。更优的办法详见第二种方式。
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
/**
* 新增自定义转换器
* @param converters {@link List<HttpMessageConverter<?>>}
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2XmlHttpMessageConverter xmlConverter = new MappingJackson2XmlHttpMessageConverter();
XmlMapper xmlMapper = new XmlMapper();
xmlConverter.setObjectMapper(xmlMapper);
//新增 xml转换器,设置顺序为0
converters.add(0,xmlConverter);
}
}
第二种方案:直接引入XML 转换器的依赖
SpringBoot项目,可以在 Pom.xml 中直接引入**" jackson-dataformat-xml"** 依赖。
<!-- Springboot默认使用json格式解析参数,如果需要支持xml入参,则需要引入 'jackson-dataformat-xml'依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<!-- 注意版本应该与 Springboot版本匹配。看SpringBoot版本发布日期去Maven找对应的版本号 -->
<version>${jackson-dataformat-xml.version}</version>
</dependency>
定义同一个名为"/test"的请求,通过指定“consumes” 和“produces”属性区分JSON格式和XML格式的控制,注意方法名要不一样。
/**
* 示例:
* 接收JSON格式的入参,并返回JSON格式的返回值
* consumes:指入参的 MediaType类型,这里设置为:接收UTF-8的JSON格式,
* produces:指返回值的MediaType类型,这里设置为:返回UTF-8的JSON格式。
*/
@PostMapping(value = "/test",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public JSONObject Json(@RequestBody JSONObject requestJsonObject) {
//业务代码,略
}
/**
* 示例:
* 接收XML格式的入参,并返回XML格式的返回值
* consumes:指入参的 MediaType类型,这里设置为:接收XML格式,
* produces:指返回值的MediaType类型,这里设置为:返回XML格式。
*/
@PostMapping(value = "/test",consumes = MediaType.APPLICATION_XML_VALUE,produces = MediaType.APPLICATION_XML_VALUE)
public JSONObject Xml(@RequestBody JSONObject requestJsonObject) {
//业务代码,略
}
三、源码分析:SpringMVC ,默认的设置的 MediaType的顺序
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
private static final boolean romePresent =
ClassUtils.isPresent("com.rometools.rome.feed.WireFeed",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jaxb2Present =
ClassUtils.isPresent("javax.xml.bind.Binder",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper",
WebMvcConfigurationSupport.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jackson2XmlPresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jackson2SmilePresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jackson2CborPresent =
ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean gsonPresent =
ClassUtils.isPresent("com.google.gson.Gson",
WebMvcConfigurationSupport.class.getClassLoader());
private static final boolean jsonbPresent =
ClassUtils.isPresent("javax.json.bind.Jsonb",
WebMvcConfigurationSupport.class.getClassLoader());
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringHttpMessageConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
// 新增 XML 转换器
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
// 新增 JSON 转换器
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
else if (gsonPresent) {
// 新增 Gson 转换器
messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
// 新增 Jsonb 转换器
messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
//新增 Smile 转换器
messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
}
if (jackson2CborPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
//新增 Cbor 转换器
messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
}
}
}
从源码看,导入了对应的依赖,比如 “jackson-dataformat-xml” ,依赖包会有 "com.fasterxml.jackson.dataformat.xml.XmlMapper"类被SpringMVC找到,
然后就会自动加到 messageConverters 转换器中,顺序就是代码add的顺序, XML > Json > Gson > Jsonb > Smile > Cbor