POJO转JSON之原理解析
相似文章有:SpringBoot内置的jackson和lombok的使用、HttpMessageConverter。
首先,新建spring项目:demo4,且添加依赖:Spring Web和lombok。
spring-boot-starter-web>spring-boot-starter-json>jackson jar,依赖关系如下。

java目录下新建包com.example.boot,并在该包下新建类:bean.Person和bean.Pet,如下。
package com.example.boot.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;
}
package com.example.boot.bean;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Pet {private String name;private Float weight;
}
com.example.boot下新建控制器类controller.Demo4Controller,如下。
package com.example.boot.controller;import com.example.boot.bean.Person;
import com.example.boot.bean.Pet;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.Date;@Controller
public class Demo4Controller {@GetMapping("/person")@ResponseBodyprivate Person save(){Person person = new Person();person.setAge(22);person.setUserName("张三");person.setBirth(new Date());person.setPet(new Pet("小苹果",8.5f));return person;}
}
resources.static下新建静态页面index.html,如下。
DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>首页title>
head>
<body><a href="/person">测试a>
body>
html>
最后,启动应用,点击测试,查看结果。

也就是,jackson jar+@ResponseBody,将Person类转换成了JSON,最终返回给了响应。
接下来探寻下原理,调试过程经过了以下部分源码。
- org.springframework.web.servlet.DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {//...// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//...// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//...
}
- org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);
}
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//...// No synchronization on session demanded at all...mav = invokeHandlerMethod(request, response, handlerMethod);//...
}
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {//...//参数解析器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}//返回值处理器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}//...invocableMethod.invokeAndHandle(webRequest, mavContainer);//...
}
返回值处理器有这么15种,如下。

- org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//...Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);//...this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);//...
}
- org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {//选择合适的返回值处理器HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}//返回值处理器对返回值进行处理handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
selectHandler,遍历现有的返回值处理器,直到找到能够处理这种类型的返回值的处理器为止;
handleReturnValue,如果找到合适的返回值处理器,则处理器调用handleReturnValue方法处理返回值。
- org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#selectHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}if (handler.supportsReturnType(returnType)) {return handler;}}return null;
}
进入HandlerMethodReturnValueHandler,该接口类有两个方法,如下。
public interface HandlerMethodReturnValueHandler {boolean supportsReturnType(MethodParameter returnType);void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}

支持处理Person类型的返回值处理器是RequestResponseBodyMethodProcessor。
- org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
- org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {//...MediaType selectedMediaType = null;acceptableTypes = getAcceptableMediaTypes(request);//...List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);//...List<MediaType> mediaTypesToUse = new ArrayList<>();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);for (MediaType mediaType : mediaTypesToUse) {if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}//...}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 {//...}}else {//...}return;}}}}
浏览器默认通过请求头(Accept)告诉服务器,自己能够接受什么样的内容类型。

服务端通过调用getAcceptableMediaTypes方法得知浏览器能够的内容类型,见acceptableTypes,有8种,如下。

服务器根据自身处理能力,决定自己能够生产什么样的内容类型。
服务器端通过调用getProducibleMediaTypes方法,确定自己能够生产的内容类型,见producibleTypes,有4种,如下。

现在,浏览器说,我能接受这8种内容类型;服务器说,我能生产这4种数据类型。二者协商下,嵌套for循环,得到mediaTypesToUse,如下。

以上4种类型,服务器能生产,浏览器也能接受。
接下来就是选择消息转换器,HttpMessageConverter。
SpringMVC默认支持10种消息转换器,如下。

进入HttpMessageConverter,其定义如下,包含5个方法。
public interface HttpMessageConverter<T> {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;
遍历当前系统现有的所有的消息转化器,找到能够将Person类转换为JSON的转换器:MappingJackson2HttpMessageConverter。

- org.springframework.http.converter.AbstractGenericHttpMessageConverter#write
public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();addDefaultHeaders(headers, t, contentType);//...writeInternal(t, type, outputMessage);outputMessage.getBody().flush();
}
- org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#writeInternal
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)throws IOException, HttpMessageNotWritableException {//...ObjectMapper objectMapper = selectObjectMapper(clazz, contentType);//...ObjectWriter objectWriter = (serializationView != null ?objectMapper.writerWithView(serializationView) : objectMapper.writer());//...objectWriter.writeValue(generator, value);writeSuffix(generator, object);generator.flush();//...
}
最后的最后,我们来了解下jackson jar、消息转换器、返回xml或json响应之间的关系。
先说结论:因为有jackson-databind、jackson-core jar包,所以消息转换器(能够将POJO转换为JSON或XML的消息转换器)才会被加入SpringMVC底层;SpringMVC底层有了这些消息转换器,才能将POJO转换为JSON或XML,并作为响应返回给浏览器。
现在来看源码,最好debug一下。

启动应用程序,自动配置类WebMvcAutoConfiguration会根据当前系统配置将现有的消息转换器加载到SpringMVC底层。其中,
只有当前系统包含com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator,变量jackson2Present才为true;jackson2Present为true时,MappingJackson2HttpMessageConverter(能把POJO转换为JSON的消息转换器)才会加入到SpringMVC底层。
只有当前系统包含com.fasterxml.jackson.dataformat.xml.XmlMapper,变量jackson2XmlPresent才为true;jackson2XmlPresent为true时,MappingJackson2XmlHttpMessageConverter(能把POJO转换为XML的消息转换器)才会加入到SpringMVC底层。
- WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#configureMessageConverters
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {this.messageConvertersProvider.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
- HttpMessageConverters#getConverters和HttpMessageConverters#getDefaultConverters
public HttpMessageConverters(boolean addDefaultConverters, Collection<HttpMessageConverter<?>> converters) {List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,addDefaultConverters ? getDefaultConverters() : Collections.emptyList());//...
}public List<HttpMessageConverter<?>> getConverters() {return this.converters;
}private List<HttpMessageConverter<?>> getDefaultConverters() {List<HttpMessageConverter<?>> converters = new ArrayList<>();if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport",null)) {converters.addAll(new WebMvcConfigurationSupport() {public List<HttpMessageConverter<?>> defaultMessageConverters() {return super.getMessageConverters();}}.defaultMessageConverters());}//...
}
- WebMvcConfigurationSupport#getMessageConverters
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;
}
- WebMvcConfigurationSupport#addDefaultHttpMessageConverters
//WebMvcConfigurationSupport
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {private static final boolean jackson2Present;private static final boolean jackson2XmlPresent;static {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);}@Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {if (jackson2XmlPresent) {messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));}if (jackson2Present) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
