文章目录
- 1. Json格式数据获取
- 2. 为什么返回Json格式的数据
- 2.1 注解SpringBootAppliaction
- 2.1.1 SpringBootConfiguration
- 2.1.2 ComponentScan
- 2.1.3 EnableAutoConfiguration
- 2.1.3.1 HttpMessageConvertersAutoConfiguration
- 2.1.3.2 WebMvcAutoConfiguration
- 2.2 注解RestController
- 2.2.1 Controller
- 2.2.2 ResponseBody
- 2.3 HttpMessageConverter
- 2.4 MappingJackson2HttpMessageConverter
- 2.5 Request header
- 2.3 总结
- 3.如何返回xml格式的数据
- 4. 如何返回自定义格式的数据
- 5. 总结
1. Json格式数据获取
在Spring boot项目中引入spring-boot-starter-web场景启动器之后,就可以轻松方便的获得json格式的数据返回。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.17</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>sj-1123</artifactId><version>0.0.1-SNAPSHOT</version><name>sj-1123</name><description>sj-1123</description><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder-jammy-base:latest</builder></image></configuration></plugin></plugins></build></project>
实体类Person.java
public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
Controller.java
@RestController
public class HelloController {@GetMapping("/hello")public ResponseEntity<Person> hello(String name, String age) {var person = new Person(name, 18);return ResponseEntity.ok(person);}
}
启动类
@SpringBootApplication
public class Sj1123Application {public static void main(String[] args) throws Exception {SpringApplication.run(Sj1123Application.class, args);}
}
这样启动后就可以通过访问localhost:8080/hello返回Json格式的结果
看到这里,会有下面的一些疑问:
- 为什么返回Json格式的数据?
- 如何返回xml格式的数据?
- 如何返回自定义格式的数据?
2. 为什么返回Json格式的数据
对于上面的spring boot项目,最主要的两个注解是SpringBootApplication和RestController
2.1 注解SpringBootAppliaction
这个是springBoot项目当中,最最常见及基础的一个注解。它用来表明这个配置类来生命一个或者多个Bean,并且触发自动配置和Bean扫描。这其实是一个复合注解,相当于同时注解了@SpringBootConfiguration, @EnableAutoConfiguration 和 @ComponentScan。
2.1.1 SpringBootConfiguration
这就是一个配置类注解,表明这是一个Spring Boot应用,可以当作是Configuration注解的一个springBoot应用中的一个替代选择。
2.1.2 ComponentScan
这个是组件扫描注解,用来指定要扫描哪些位置下的类,这样可以让spring 容器来找到这些BeanDefinition并生成Bean。
2.1.3 EnableAutoConfiguration
这个注解是spring Boot项目能够进行自动装配的关键。启用 Spring Application Context 的自动配置,尝试猜测和配置您可能需要的 bean。自动配置类通常基于您的类路径和您定义的 Bean 来应用。
这个注解上有一个注解Import,这个也是用来配置Bean的一种方式,AutoConfigurationImportSelector.class 这个类可以看到将会选择哪些Bean需要自动装配进来。
@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}
深入阅读源码,可以发现,spring自动加载位于"META-INF/spring/%s.imports"里定义的自动配置类。org.springframework.boot.autoconfigure.AutoConfiguration.imports 这个文件里共有144个自动配置类,涵盖了常见的Redis,MongoDB, JDBC,es, Cassandra,web等等。
这里有两个需要关注一下:
- org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration 这个是用来自动配置HttpMessageConverter的。
- org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration Auto-configuration for Web MVC.
2.1.3.1 HttpMessageConvertersAutoConfiguration
这个配置类里,可以加载两个Bean:
@Bean@ConditionalOnMissingBeanpublic HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));}@Configuration(proxyBeanMethods = false)@ConditionalOnClass(StringHttpMessageConverter.class)protected static class StringHttpMessageConverterConfiguration {@Bean@ConditionalOnMissingBeanpublic StringHttpMessageConverter stringHttpMessageConverter(Environment environment) {Encoding encoding = Binder.get(environment).bindOrCreate("server.servlet.encoding", Encoding.class);StringHttpMessageConverter converter = new StringHttpMessageConverter(encoding.getCharset());converter.setWriteAcceptCharset(false);return converter;}}
一个是StringHttpMesageConverter类型的Bean,这个Bean加载的要求是当前类StringHttpMessageConverter.class存在,且Bean还没有,那就加载这个StringHttpMessageConverter的Bean。
另一个是HttpMessageConverters的Bean。
另外,这个配置类还会由Import注解导入另外三个配置类:
- JacksonHttpMessageConvertersConfiguration.class
- GsonHttpMessageConvertersConfiguration.class
- JsonbHttpMessageConvertersConfiguration.class
Jackson的配置类里面,会加载MappingJackson2HttpMessageConverter,这个Converter就是用来转Json的一个重要转换类。由于没有引入Gson和Jsonb相关的包,也不会加载这两个配置类里的相关内容。
2.1.3.2 WebMvcAutoConfiguration
这个配置类,用来配置web mvc相关的内容,但是是要在WebMvcConfigurationSupport.class这个类型的Bean不存在,且Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class 这三个类存在,而且是servlet的web应用下,才会用到这个配置类。
但是WebMvcConfigurationSupport这个类型的Bean是什么时候在哪里指示加载的呢?
This is the main class providing the configuration behind the MVC Java config. It is typically imported by adding @EnableWebMvc to an application @Configuration class. An alternative more advanced option is to extend directly from this class and override methods as necessary, remembering to add @Configuration to the subclass and @Bean to overridden @Bean methods. For more details see the javadoc of @EnableWebMvc.
可以看到是在配置类上启用EnableWebMVC注解。或者是直接扩展这个类,然后加载Bean。
而在WebMvcAutoConfiguration.class里面,由这样一段代码
/*** Configuration equivalent to {@code @EnableWebMvc}.*/@Configuration(proxyBeanMethods = false)@EnableConfigurationProperties(WebProperties.class)public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {private final Resources resourceProperties;private final WebMvcProperties mvcProperties;private final WebProperties webProperties;private final ListableBeanFactory beanFactory;private final WebMvcRegistrations mvcRegistrations;private ResourceLoader resourceLoader;public EnableWebMvcConfiguration(WebMvcProperties mvcProperties, WebProperties webProperties,ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,ListableBeanFactory beanFactory) {this.resourceProperties = webProperties.getResources();this.mvcProperties = mvcProperties;this.webProperties = webProperties;this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();this.beanFactory = beanFactory;}
这就相当于启用了EnableWebMVC注解,加载了WebMvcConfigurationSupport类型的Bean。这个类型的Bean,配置了非常多的web相关的内容:
配置messageConverter相关的内容
/*** Provides access to the shared {@link HttpMessageConverter HttpMessageConverters}* used by the {@link RequestMappingHandlerAdapter} and the* {@link ExceptionHandlerExceptionResolver}.* <p>This method cannot be overridden; use {@link #configureMessageConverters} instead.* Also see {@link #addDefaultHttpMessageConverters} for adding default message converters.*/protected final List<HttpMessageConverter<?>> getMessageConverters() {if (this.messageConverters == null) {this.messageConverters = new ArrayList<>();configureMessageConverters(this.messageConverters);if (this.messageConverters.isEmpty()) {addDefaultHttpMessageConverters(this.messageConverters);}extendMessageConverters(this.messageConverters);}return this.messageConverters;}/*** Override this method to add custom {@link HttpMessageConverter HttpMessageConverters}* to use with the {@link RequestMappingHandlerAdapter} and the* {@link ExceptionHandlerExceptionResolver}.* <p>Adding converters to the list turns off the default converters that would* otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters}* for adding default message converters.* @param converters a list to add message converters to (initially an empty list)*/protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}/*** Override this method to extend or modify the list of converters after it has* been configured. This may be useful for example to allow default converters* to be registered and then insert a custom converter through this method.* @param converters the list of configured converters to extend* @since 4.1.3*/protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}/*** Adds a set of default HttpMessageConverter instances to the given list.* Subclasses can call this method from {@link #configureMessageConverters}.* @param messageConverters the list to add the default message converters to*/protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {messageConverters.add(new ByteArrayHttpMessageConverter());messageConverters.add(new StringHttpMessageConverter());messageConverters.add(new ResourceHttpMessageConverter());messageConverters.add(new ResourceRegionHttpMessageConverter());if (!shouldIgnoreXml) {try {messageConverters.add(new SourceHttpMessageConverter<>());}catch (Error err) {// Ignore when no TransformerFactory implementation is available}}messageConverters.add(new AllEncompassingFormHttpMessageConverter());if (romePresent) {messageConverters.add(new AtomFeedHttpMessageConverter());messageConverters.add(new RssChannelHttpMessageConverter());}if (!shouldIgnoreXml) {if (jackson2XmlPresent) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();if (this.applicationContext != null) {builder.applicationContext(this.applicationContext);}messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));}else if (jaxb2Present) {messageConverters.add(new Jaxb2RootElementHttpMessageConverter());}}if (kotlinSerializationJsonPresent) {messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());}if (jackson2Present) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();if (this.applicationContext != null) {builder.applicationContext(this.applicationContext);}messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));}else if (gsonPresent) {messageConverters.add(new GsonHttpMessageConverter());}else if (jsonbPresent) {messageConverters.add(new JsonbHttpMessageConverter());}if (jackson2SmilePresent) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();if (this.applicationContext != null) {builder.applicationContext(this.applicationContext);}messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));}if (jackson2CborPresent) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();if (this.applicationContext != null) {builder.applicationContext(this.applicationContext);}messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));}}
这一共三个方法:
- 第一个是直接Config custom的MessageConverter,这个就意味着默认的MessageConverter将失效了,只能用自定义的。
- 第二个是extend MessageConverter, 这个可以添加自己的MessageConverter的同时,保留原有的MessageConverter。
- 第三个是直接使用默认的MessageConverter。默认的一共有哪些呢?这些由spring容器根据类是否出现在class path中来进行判断。
static {ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);}
这里可以看到默认加载的MessageConverter有:
- ByteArrayHttpMessageConverter
- StringHttpMessageConverter
- ResourceHttpMessageConverter
- ResourceRegionHttpMessageConverter
- AllEncompassingFormHttpMessageConverter
- MappingJackson2HttpMessageConverter
- SourceHttpMessageConverter
所有默认的MessageConverter已经就位了,那什么时候使用呢?这个时候就要看另一个注解RestController了。
2.2 注解RestController
这也是一个复合注解,由两个注解组成:
@Controller
@ResponseBody
2.2.1 Controller
这个是一个特别熟悉的注解,就是表示这是一个控制器controller,它和RequestMapping注解一起,处理请求。
2.2.2 ResponseBody
这个注解就是表明返回值直接绑定到response body中。当使用@ResponseBody注解时,Spring MVC会根据请求的Content-Type头和处理方法的返回类型来选择合适的HttpMessageConverter进行数据转换。HttpMessageConverter负责将Java对象转换为响应的数据格式,并将其写入HTTP响应中。
2.3 HttpMessageConverter
HttpMessageConverter负责将对象转化为对应的数据格式,那是如何做到的呢?
public interface HttpMessageConverter<T> {boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);List<MediaType> getSupportedMediaTypes();default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {return (canRead(clazz, null) || canWrite(clazz, null) ?getSupportedMediaTypes() : Collections.emptyList());}T read(Class<? extends T> clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException;void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException; }
五个方法,一个是看支持哪些MediaType,剩下4个事两组,一组是读,一组是写。在读写之前要check是都可以读、写。
简单的类图关系如下:
这里是一个标准的策略模式的实现。在处理类AbstractMessageConverterMethodProcessor.class中,依次使用MessageConverter来进行判断是否可以write,可以就将结果写入到响应体中。
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class<?> valueType;Type targetType;if (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;}else {body = value;valueType = getReturnValueType(body, returnType);targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}if (isResourceType(value, returnType)) {outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource) value;try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());body = HttpRange.toResourceRegions(httpRanges, resource);valueType = body.getClass();targetType = RESOURCE_REGION_LIST_TYPE;}catch (IllegalArgumentException ex) {outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());}}}MediaType selectedMediaType = null;MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType != null && contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug("Found 'Content-Type:" + contentType + "' in response");}selectedMediaType = contentType;}else {HttpServletRequest request = inputMessage.getServletRequest();List<MediaType> acceptableTypes;try {acceptableTypes = getAcceptableMediaTypes(request);}catch (HttpMediaTypeNotAcceptableException ex) {int series = outputMessage.getServletResponse().getStatus() / 100;if (body == null || series == 4 || series == 5) {if (logger.isDebugEnabled()) {logger.debug("Ignoring error response content (if any). " + ex);}return;}throw ex;}List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}List<MediaType> mediaTypesToUse = new ArrayList<>();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (logger.isDebugEnabled()) {logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);}if (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);//这里选择出要输出的MediaType类型,这个是由发起请求的Request header里面Accepte 决定的。for (MediaType mediaType : mediaTypesToUse) {if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug("Using '" + selectedMediaType + "', given " +acceptableTypes + " and supported " + producibleTypes);}}//这里遍历spring加载的所有MessageConverter,依次遍历,如果是可以write,就调用这个MessageConverter的write方法,把结果写入response body中。写成什么样的格式,由当前这个MessageConverter的write方法决定。if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}if (body != null) {Set<MediaType> producibleMediaTypes =(Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");}throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));}}
2.4 MappingJackson2HttpMessageConverter
这个是spring 官方默认的Json格式MessageConverter。这是Jackson的工具,由ObjectMapper来写出writeValue。
2.5 Request header
我们在使用postman发送请求时,没有注意设置的请求头是什么,但是里面是有一些默认值的:
甚至我们使用curl指令的时候,也可以不写这些信息
curl --location 'localhost:10083/hello?name=zhangsan'
这是因为Accept的值为*/*和缺省是一样的,这个结果被默认为返回appliaction/json格式数据。
2.3 总结
至此,可以说算是明白为什么返回Json格式数据了:
我们给服务器说,接收*/*格式数据的response。
默认的HttpMessageConverter把JAVA对象转成了Json格式写入response body中。
3.如何返回xml格式的数据
这个时候,应该已经很明确了:
- 1.你要告诉服务端,你需要xml格式的数据:设置Request header的Accept值为application/xml
-
- 配置好服务端的MessageConverter,让它将JAVA对象转成xml格式。
Jackson已经可以支持返回xml格式数据了,只需要引入相应依赖即可。
- 配置好服务端的MessageConverter,让它将JAVA对象转成xml格式。
在pom.xml文件中引入xml格式依赖
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId></dependency>
然后再entity类加上注解
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;@JacksonXmlRootElement
public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
这个时候再发送请求:
可以看到,已经可以返回XML格式数据了。
4. 如何返回自定义格式的数据
返回自定义格式的数据,就是要和服务端约定好,是什么样的格式,然后有converter写出来即可。
从上面的类图可以看到,我们可以直接实现HttpMessageConverter接口,也可以继承类图里的抽象类也可以。
这里提供一个继承AbstractHttpMessageConverter的例子,实现返回yaml格式:
package com.example.sj1123.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;public class YamlMessageConverter extends AbstractHttpMessageConverter<Object> {private ObjectMapper objectMapper = null;public YamlMessageConverter() {super(new MediaType("application", "yaml", StandardCharsets.UTF_8));YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);this.objectMapper = new ObjectMapper(factory);}@Overrideprotected boolean supports(Class<?> clazz) {return true;}@Overrideprotected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {return null;}@Overrideprotected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {try (OutputStream outputStream = outputMessage.getBody()) {this.objectMapper.writeValue(outputStream, o);}}
}
然后再配置这个MessageConverter作为Bean
@Configuration
public class AppConfig {@Beanpublic WebMvcConfigurer webMvcConfigurer() {return new WebMvcConfigurer() {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new YamlMessageConverter());}};}
}
这个时候,再启动应用,就可以返回yaml格式的数据了
5. 总结
HttpMessageConverter可以让读取参数、返回请求转换成指定格式。在服务端可以配置多中格式的MessageConverter。
另外,指定格式的方式不仅仅只有通过Request Header这个方式,也可以通过参数 type=yaml这种格式。