以下面的代码为例:

@RestController
public class HelloController {@RequestMapping("/")public BillSearch hello(@RequestBody BillSearch search) {return search;}
}

前端通过Postman进行模拟:

下面开始一步步的揭开它的面纱:

先从HandlerMethodArgumentResolverComposite开始:

    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +" supportsParameter should be called first.");}return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}

resolver为RequestResponseBodyMethodProcessor 这个类是序列化和反序列化常用到的类。下面是它的resolveArgument方法:

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {parameter = parameter.nestedIfOptional();  Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name = Conventions.getVariableNameForParameter(parameter);if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if (arg != null) {validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return adaptArgumentIfNecessary(arg, parameter);}

readWithMessageConverters方法如下:
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);Assert.state(servletRequest != null, "No HttpServletRequest");ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);        Object arg = readWithMessageConverters(inputMessage, parameter, paramType);if (arg == null && checkRequired(parameter)) {throw new HttpMessageNotReadableException("Required request body is missing: " +parameter.getExecutable().toGenericString(), inputMessage);}return arg;}

 
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {MediaType contentType;boolean noContentType = false;try {contentType = inputMessage.getHeaders().getContentType();}catch (InvalidMediaTypeException ex) {throw new HttpMediaTypeNotSupportedException(ex.getMessage());}if (contentType == null) {noContentType = true;contentType = MediaType.APPLICATION_OCTET_STREAM;}Class<?> contextClass = parameter.getContainingClass();Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);if (targetClass == null) {ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);targetClass = (Class<T>) resolvableType.resolve();}HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);Object body = NO_VALUE;EmptyBodyCheckingHttpInputMessage message;try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}catch (IOException ex) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);}if (body == NO_VALUE) {if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||(noContentType && !message.hasBody())) {return null;}throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);}MediaType selectedContentType = contentType;Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(theBody, !traceOn);return "Read \"" + selectedContentType + "\" to [" + formatted + "]";});return body;}

上一篇博客里介绍了messageConverters,在项目启动时添加了MappingJackson2HttpMessageConverter,这里主是就是找到这个converter对参数进行解析:再进一步追踪:在AbstractJackson2HttpMessageConverter类中,就找到了我们要找到objectMapper:
private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {try {if (inputMessage instanceof MappingJacksonInputMessage) {Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();if (deserializationView != null) {return this.objectMapper.readerWithView(deserializationView).forType(javaType).readValue(inputMessage.getBody());}}return this.objectMapper.readValue(inputMessage.getBody(), javaType);}catch (InvalidDefinitionException ex) {throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);}catch (JsonProcessingException ex) {throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex, inputMessage);}}

顺便再介绍一下objectMapper反序列化的主要步骤:

protected Object _readMapAndClose(JsonParser p0, JavaType valueType)throws IOException{try (JsonParser p = p0) {Object result;JsonToken t = _initForReading(p, valueType);final DeserializationConfig cfg = getDeserializationConfig();final DeserializationContext ctxt = createDeserializationContext(p, cfg);if (t == JsonToken.VALUE_NULL) {// Ask JsonDeserializer what 'null value' to use:result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {result = null;} else { //com.fasterxml.jackson.databind.deser.BeanDeserializerJsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);if (cfg.useRootWrapping()) {result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);} else {result = deser.deserialize(p, ctxt);}ctxt.checkUnresolvedObjectId();}if (cfg.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {_verifyNoTrailingTokens(p, ctxt, valueType);}return result;}}

public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException{// common case firstif (p.isExpectedStartObjectToken()) {if (_vanillaProcessing) {return vanillaDeserialize(p, ctxt, p.nextToken());}// 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is//    what it is, including "expected behavior".
            p.nextToken();if (_objectIdReader != null) {return deserializeWithObjectId(p, ctxt);}return deserializeFromObject(p, ctxt);}return _deserializeOther(p, ctxt, p.getCurrentToken());}

if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {String propName = p.getCurrentName();do {p.nextToken(); //根据获取属性名,获取这个属性SettableBeanProperty prop = _beanProperties.find(propName);if (prop != null) { // normal casetry { prop.deserializeAndSet(p, ctxt, bean);} catch (Exception e) {wrapAndThrow(e, bean, propName, ctxt);}continue;}handleUnknownVanilla(p, ctxt, bean, propName);} while ((propName = p.nextFieldName()) != null);}return bean;

针对这个属性进行反序列化解析,由于这个属性是个枚举,所以它的_valueDeserializer是com.fasterxml.jackson.databind.deser.std.EnumDeserializer

public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,Object instance) throws IOException{Object value;if (p.hasToken(JsonToken.VALUE_NULL)) {if (_skipNulls) {return;}value = _nullProvider.getNullValue(ctxt);} else if (_valueTypeDeserializer == null) {       //com.fasterxml.jackson.databind.deser.std.EnumDeserializervalue = _valueDeserializer.deserialize(p, ctxt);// 04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give nullif (value == null) {if (_skipNulls) {return;}value = _nullProvider.getNullValue(ctxt);}} else {value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);}try {_setter.invoke(instance, value);} catch (Exception e) {_throwAsIOE(p, e, value);}}

那为什么jackson枚举的反序列化默认用的是EnumDeserializer呢?

这要回到文章开始的地方说起:在一步中会判断指定的类型是否能够进行canRead()

for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}

就从canRead()方法说起:

public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {if (!canRead(mediaType)) {return false;}JavaType javaType = getJavaType(type, contextClass);AtomicReference<Throwable> causeRef = new AtomicReference<>();if (this.objectMapper.canDeserialize(javaType, causeRef)) {return true;}logWarningIfNecessary(javaType, causeRef.get());return false;}

public boolean canDeserialize(JavaType type, AtomicReference<Throwable> cause){return createDeserializationContext(null,getDeserializationConfig()).hasValueDeserializerFor(type, cause);}

public boolean hasValueDeserializerFor(JavaType type, AtomicReference<Throwable> cause) {try {return _cache.hasValueDeserializerFor(this, _factory, type);} ...}

public boolean hasValueDeserializerFor(DeserializationContext ctxt,DeserializerFactory factory, JavaType type)throws JsonMappingException{/* Note: mostly copied from findValueDeserializer, except for* handling of unknown types*/JsonDeserializer<Object> deser = _findCachedDeserializer(type);if (deser == null) {deser = _createAndCacheValueDeserializer(ctxt, factory, type);}return (deser != null);}

注意这个名称createAndCache它是会缓存的,也就是这个类型只会找一次,找到之后,就它的反序列化类就绑定了,缓存起来了,

这们有时配置 jackson的objectMapper,可能会添加很多反序列化的模块,都会注册到_factoryConfig.deserializers()

protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,DeserializationConfig config, BeanDescription beanDesc)throws JsonMappingException{for (Deserializers d  : _factoryConfig.deserializers()) {JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);if (deser != null) {return (JsonDeserializer<Object>) deser;}}return null;}

private final JsonDeserializer<?> _find(JavaType type) {if (_classMappings == null) {return null;}return _classMappings.get(new ClassKey(type.getRawClass()));}

每个模块都有_classMappings这样的集合,记录着类与它的序列化类的对应关系。由于我们没有定义这样的关系,那它就有一个默认的。

再找到这个类的反序列化类以后,再开始找类里面的属性的:

同样是在DeserializerCache类中的_createAndCache2方法:

protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,DeserializerFactory factory, JavaType type)throws JsonMappingException{JsonDeserializer<Object> deser;try {deser = _createDeserializer(ctxt, factory, type);} catch (IllegalArgumentException iae) {// We better only expose checked exceptions, since those// are what caller is expected to handlethrow JsonMappingException.from(ctxt, ClassUtil.exceptionMessage(iae), iae);}if (deser == null) {return null;}/* cache resulting deserializer? always true for "plain" BeanDeserializer* (but can be re-defined for sub-classes by using @JsonCachable!)*/// 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desersboolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();/* we will temporarily hold on to all created deserializers (to* handle cyclic references, and possibly reuse non-cached* deserializers (list, map))*//* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental*   resolution of a reference -- couple of ways to prevent this;*   either not add Lists or Maps, or clear references eagerly.*   Let's actually do both; since both seem reasonable.*//* Need to resolve? Mostly done for bean deserializers; required for* resolving cyclic references.*/if (deser instanceof ResolvableDeserializer) {_incompleteDeserializers.put(type, deser);((ResolvableDeserializer)deser).resolve(ctxt);_incompleteDeserializers.remove(type);}if (addToCache) {_cachedDeserializers.put(type, deser);}return deser;}

遍历每一个属性:

for (SettableBeanProperty prop : _beanProperties) {if (!prop.hasValueDeserializer()) {// [databind#125]: allow use of convertersJsonDeserializer<?> deser = findConvertingDeserializer(ctxt, prop);if (deser == null) {deser = ctxt.findNonContextualValueDeserializer(prop.getType());}SettableBeanProperty newProp = prop.withValueDeserializer(deser);_replaceProperty(_beanProperties, creatorProps, prop, newProp);}}

type.isEnumType()这里是关键:
protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,DeserializerFactory factory, JavaType type, BeanDescription beanDesc)throws JsonMappingException{final DeserializationConfig config = ctxt.getConfig();// If not, let's see which factory method to use: if (type.isEnumType()) {return factory.createEnumDeserializer(ctxt, type, beanDesc);}。。。}

public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,JavaType type, BeanDescription beanDesc)throws JsonMappingException{final DeserializationConfig config = ctxt.getConfig();final Class<?> enumClass = type.getRawClass();// 23-Nov-2010, tatu: Custom deserializer?JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc);if (deser == null) {ValueInstantiator valueInstantiator = _constructDefaultValueInstantiator(ctxt, beanDesc);SettableBeanProperty[] creatorProps = (valueInstantiator == null) ? null: valueInstantiator.getFromObjectArguments(ctxt.getConfig());// May have @JsonCreator for static factory method:  //这里是重点,如上面的注释,如果在enum中定义了工厂方法,找打上了JsonCreator的话,那就算指定了反序列化的方法了,会通过反射执行反序列化
             for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {if (_hasCreatorAnnotation(ctxt, factory)) {if (factory.getParameterCount() == 0) { // [databind#960]deser = EnumDeserializer.deserializerForNoArgsCreator(config, enumClass, factory);break;}Class<?> returnType = factory.getRawReturnType();// usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)if (returnType.isAssignableFrom(enumClass)) {deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory, valueInstantiator, creatorProps);break;}}}// Need to consider @JsonValue if one foundif (deser == null) {deser = new EnumDeserializer(constructEnumResolver(enumClass,config, beanDesc.findJsonValueAccessor()),config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));}}// and then post-process it tooif (_factoryConfig.hasDeserializerModifiers()) {for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {deser = mod.modifyEnumDeserializer(config, type, beanDesc, deser);}}return deser;}

_findCustomEnumDeserializer又云objectMapper中的配置中找:由于没有配置,所以返回null
protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,DeserializationConfig config, BeanDescription beanDesc)throws JsonMappingException{for (Deserializers d  : _factoryConfig.deserializers()) {JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);if (deser != null) {return deser;}}return null;}

由一没找到,所以就指定了EnumDeserializer为枚举的默认反序列化类了。

转载于:https://www.cnblogs.com/hankuikui/p/11592724.html

spring boot 是如何利用jackson进行反序列化的?相关推荐

  1. spring boot 是如何利用jackson进行序列化的?

    接上一篇:spring boot 是如何利用jackson进行反序列化的? @RestController public class HelloController {@RequestMapping( ...

  2. Spring Boot框架中使用Jackson的处理总结

    1.前言 通常我们在使用Spring Boot框架时,如果没有特别指定接口的序列化类型,则会使用Spring Boot框架默认集成的Jackson框架进行处理,通过Jackson框架将服务端响应的数据 ...

  3. spring boot java app_利用spring boot创建java app

    利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...

  4. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式1

    0. 前言 之前帐号认证用过自己写的进行匹配,现在要学会使用标准了.准备了解和使用这个OAuth2.0协议. 1. 配置 1.1 配置pom.xml 有些可能会用不到,我把我项目中用到的所有包都贴出来 ...

  5. SpringBoot学习之路:06.Spring Boot替换默认的Jackson

    2019独角兽企业重金招聘Python工程师标准>>> SpringBoot和Springmvc都可以返回接送数据,SpringBoot默认是使用Jackson解析json数据的,个 ...

  6. Spring Boot(四):利用devtools实现热部署,改动代码自动生效

    一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...

  7. Spring Boot jackson配置使用详解

    Spring Boot系列-json框架jackson配置详解 T1 - 前言 目前Java最常见的3中JSON操作框架分别为Gson.Jackson.FastJson,该篇文章主要讲解jackson ...

  8. Spring Boot 利用WebUploader进行文件上传

    Web Uploader WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优势 ...

  9. move_uploaded_file返回false但实际成功_023 Spring Boot 搭建实际项目开发框架

    前面的课程中,我主要给大家讲解了 Spring Boot 中常用的一些技术点,这些技术点在实际项目中可能不会全部用得到,因为不同的项目可能使用的技术不同,但是希望大家都能掌握如何使用,并能自己根据实际 ...

最新文章

  1. vim 命令模式与输入模式切换
  2. DetNAS ThunderNet
  3. Java中通过substring和charAt截取字符串并获取指定字符
  4. 数据结构录 之 单调队列单调栈。
  5. CDH 版本Hadoop Yarn配置容量调度器(默认是公平调度器)
  6. 服务器被攻击怎么办 安全狗来防护
  7. 读书笔记——信息的表示与处理
  8. LSTM block和cell区别
  9. (转)Faster R-CNN的训练过程的理解
  10. 贪心整理一本通1431:钓鱼题解
  11. 算法设计与分析——图像的压缩
  12. 服务器系统怎么做ghost备份,手动ghost备份系统的具体操作步骤【图文教程】
  13. delphi软件工程师面试试题
  14. H5游戏开发-Egret引擎
  15. Windows操作系统注册表registry
  16. 电脑鼠标右键菜单反应慢怎么办?右键菜单反应慢解决办法
  17. python网络编程好学吗_年薪30万+的python编程难不难?好学吗?
  18. 前端案例:蓝色CSS3二级导航菜单
  19. 主题计算机一级,Office2007文档主题的应用或自定义
  20. 计算机技术培训新闻,二附院举办ERAS下人工智能计算机辅助关节置换技术

热门文章

  1. 基于nginx-rtmp-module搭建直播系统
  2. 在老家最长的寒假之旅
  3. 科技巨头之间的生态系统之战
  4. 读书软件源码,实现书籍阅读的其他功能
  5. HarmonyOS实战—欧洲杯还可以这么玩?
  6. vc不支援此种界接口_“凡尔赛”文学冲入护肤界,名媛们都开始紧张了
  7. 自监督表征学习方法——BYOL(Bootstrap Your Own Latent)
  8. 学java还是平面设计好_平面设计师精品课程
  9. vue下载文件流完整前后端代码
  10. oracle中=符号的意义