irpas技术客

POJO转JSON之原理解析_pojo to json_richest_qi

irpas 2488

相似文章有: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") @ResponseBody private 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); } @Bean public 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())); } }


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #pojo #To #JSON #Web和lombok #jar依赖关系如下