spring boot 是如何利用jackson进行反序列化的?
以下面的代码为例:
@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进行反序列化的?相关推荐
- spring boot 是如何利用jackson进行序列化的?
接上一篇:spring boot 是如何利用jackson进行反序列化的? @RestController public class HelloController {@RequestMapping( ...
- Spring Boot框架中使用Jackson的处理总结
1.前言 通常我们在使用Spring Boot框架时,如果没有特别指定接口的序列化类型,则会使用Spring Boot框架默认集成的Jackson框架进行处理,通过Jackson框架将服务端响应的数据 ...
- spring boot java app_利用spring boot创建java app
利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...
- Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式1
0. 前言 之前帐号认证用过自己写的进行匹配,现在要学会使用标准了.准备了解和使用这个OAuth2.0协议. 1. 配置 1.1 配置pom.xml 有些可能会用不到,我把我项目中用到的所有包都贴出来 ...
- SpringBoot学习之路:06.Spring Boot替换默认的Jackson
2019独角兽企业重金招聘Python工程师标准>>> SpringBoot和Springmvc都可以返回接送数据,SpringBoot默认是使用Jackson解析json数据的,个 ...
- Spring Boot(四):利用devtools实现热部署,改动代码自动生效
一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...
- Spring Boot jackson配置使用详解
Spring Boot系列-json框架jackson配置详解 T1 - 前言 目前Java最常见的3中JSON操作框架分别为Gson.Jackson.FastJson,该篇文章主要讲解jackson ...
- Spring Boot 利用WebUploader进行文件上传
Web Uploader WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件.在现代的浏览器里面能充分发挥HTML5的优势 ...
- move_uploaded_file返回false但实际成功_023 Spring Boot 搭建实际项目开发框架
前面的课程中,我主要给大家讲解了 Spring Boot 中常用的一些技术点,这些技术点在实际项目中可能不会全部用得到,因为不同的项目可能使用的技术不同,但是希望大家都能掌握如何使用,并能自己根据实际 ...
最新文章
- vim 命令模式与输入模式切换
- DetNAS ThunderNet
- Java中通过substring和charAt截取字符串并获取指定字符
- 数据结构录 之 单调队列单调栈。
- CDH 版本Hadoop Yarn配置容量调度器(默认是公平调度器)
- 服务器被攻击怎么办 安全狗来防护
- 读书笔记——信息的表示与处理
- LSTM block和cell区别
- (转)Faster R-CNN的训练过程的理解
- 贪心整理一本通1431:钓鱼题解
- 算法设计与分析——图像的压缩
- 服务器系统怎么做ghost备份,手动ghost备份系统的具体操作步骤【图文教程】
- delphi软件工程师面试试题
- H5游戏开发-Egret引擎
- Windows操作系统注册表registry
- 电脑鼠标右键菜单反应慢怎么办?右键菜单反应慢解决办法
- python网络编程好学吗_年薪30万+的python编程难不难?好学吗?
- 前端案例:蓝色CSS3二级导航菜单
- 主题计算机一级,Office2007文档主题的应用或自定义
- 计算机技术培训新闻,二附院举办ERAS下人工智能计算机辅助关节置换技术