jackson 反序列化string_spring boot 是如何利用jackson进行反序列化的?

以下面的代码为例:

@RestControllerpublic classHelloController {

@RequestMapping("/")publicBillSearch hello(@RequestBody BillSearch search) {returnsearch;

}

}

前端通过Postman进行模拟:

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

先从HandlerMethodArgumentResolverComposite开始:

publicObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,

NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)throwsException {

HandlerMethodArgumentResolver resolver=getArgumentResolver(parameter);if (resolver == null) {throw newIllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +

" supportsParameter should be called first.");

}returnresolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);

}

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

publicObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,

NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)throwsException {

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 newMethodArgumentNotValidException(parameter, binder.getBindingResult());

}

}if (mavContainer != null) {

mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX+name, binder.getBindingResult());

}

}returnadaptArgumentIfNecessary(arg, parameter);

}

readWithMessageConverters方法如下:

protected Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,

Type paramType)throwsIOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

HttpServletRequest servletRequest= webRequest.getNativeRequest(HttpServletRequest.class);

Assert.state(servletRequest!= null, "No HttpServletRequest");

ServletServerHttpRequest inputMessage= newServletServerHttpRequest(servletRequest);

Object arg= readWithMessageConverters(inputMessage, parameter, paramType);if (arg == null &&checkRequired(parameter)) {throw new HttpMessageNotReadableException("Required request body is missing: " +parameter.getExecutable().toGenericString(), inputMessage);

}returnarg;

}

protected Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,

Type targetType)throwsIOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

MediaType contentType;boolean noContentType = false;try{

contentType=inputMessage.getHeaders().getContentType();

}catch(InvalidMediaTypeException ex) {throw newHttpMediaTypeNotSupportedException(ex.getMessage());

}if (contentType == null) {

noContentType= true;

contentType=MediaType.APPLICATION_OCTET_STREAM;

}

Class> contextClass =parameter.getContainingClass();

Class targetClass = (targetType instanceof Class ? (Class) targetType : null);if (targetClass == null) {

ResolvableType resolvableType=ResolvableType.forMethodParameter(parameter);

targetClass= (Class) resolvableType.resolve();

}

HttpMethod httpMethod= (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);

Object body=NO_VALUE;

EmptyBodyCheckingHttpInputMessage message;try{

message= newEmptyBodyCheckingHttpInputMessage(inputMessage);for (HttpMessageConverter> converter : this.messageConverters) {

Class> converterType = (Class>) 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) 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 + "]";

});returnbody;

}

上一篇博客里介绍了messageConverters,在项目启动时添加了MappingJackson2HttpMessageConverter,这里主是就是找到这个converter对参数进行解析:

再进一步追踪:在AbstractJackson2HttpMessageConverter类中,就找到了我们要找到objectMapper:

private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throwsIOException {try{if (inputMessage instanceofMappingJacksonInputMessage) {

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反序列化的主要步骤:

protectedObject _readMapAndClose(JsonParser p0, JavaType valueType)throwsIOException

{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.BeanDeserializer

JsonDeserializer 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);

}returnresult;

}

}

public Object deserialize(JsonParser p, DeserializationContext ctxt) throwsIOException

{//common case first

if(p.isExpectedStartObjectToken()) {if(_vanillaProcessing) {returnvanillaDeserialize(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) {returndeserializeWithObjectId(p, ctxt);

}returndeserializeFromObject(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 case

try{

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 voiddeserializeAndSet(JsonParser p, DeserializationContext ctxt,

Object instance)throwsIOException

{

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.EnumDeserializer

value= _valueDeserializer.deserialize(p, ctxt);//04-May-2018, tatu: [databind#2023] Coercion from String (mostly) can give null

if (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> converterType = (Class>) converter.getClass();

GenericHttpMessageConverter> genericConverter =(converterinstanceof 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) 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 causeRef = new AtomicReference<>();if (this.objectMapper.canDeserialize(javaType, causeRef)) {

return true;

}

logWarningIfNecessary(javaType, causeRef.get());return false;

}

public boolean canDeserialize(JavaType type, AtomicReferencecause)

{return createDeserializationContext(null,

getDeserializationConfig()).hasValueDeserializerFor(type, cause);

}

public boolean hasValueDeserializerFor(JavaType type, AtomicReferencecause) {try{return _cache.hasValueDeserializerFor(this, _factory, type);

} ...}

public booleanhasValueDeserializerFor(DeserializationContext ctxt,

DeserializerFactory factory, JavaType type)throwsJsonMappingException

{/*Note: mostly copied from findValueDeserializer, except for

* handling of unknown types*/JsonDeserializer deser =_findCachedDeserializer(type);if (deser == null) {

deser=_createAndCacheValueDeserializer(ctxt, factory, type);

}return (deser != null);

}

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

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

protected JsonDeserializer_findCustomBeanDeserializer(JavaType type,

DeserializationConfig config, BeanDescription beanDesc)throwsJsonMappingException

{for (Deserializers d : _factoryConfig.deserializers()) {

JsonDeserializer> deser = d.findBeanDeserializer(type, config, beanDesc);

if (deser != null) {

return (JsonDeserializer) deser;

}

}return null;

}

private final JsonDeserializer>_find(JavaType type) {if (_classMappings == null) {return null;

}return _classMappings.get(newClassKey(type.getRawClass()));

}

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

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

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

protected JsonDeserializer_createAndCache2(DeserializationContext ctxt,

DeserializerFactory factory, JavaType type)throwsJsonMappingException

{

JsonDeserializerdeser;try{

deser=_createDeserializer(ctxt, factory, type);

}catch(IllegalArgumentException iae) {//We better only expose checked exceptions, since those//are what caller is expected to handle

throwJsonMappingException.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 desers

boolean 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 instanceofResolvableDeserializer) {

_incompleteDeserializers.put(type, deser);

((ResolvableDeserializer)deser).resolve(ctxt);

_incompleteDeserializers.remove(type);

}if(addToCache) {

_cachedDeserializers.put(type, deser);

}returndeser;

}

遍历每一个属性:

for (SettableBeanProperty prop : _beanProperties) {if (!prop.hasValueDeserializer()) {//[databind#125]: allow use of converters

JsonDeserializer> 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)throwsJsonMappingException

{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)throwsJsonMappingException

{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 found

if (deser == null) {

deser = newEnumDeserializer(constructEnumResolver(enumClass,

config, beanDesc.findJsonValueAccessor()),

config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS));

}

}//and then post-process it too

if(_factoryConfig.hasDeserializerModifiers()) {for(BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {

deser=mod.modifyEnumDeserializer(config, type, beanDesc, deser);

}

}returndeser;

}

_findCustomEnumDeserializer又云objectMapper中的配置中找:由于没有配置,所以返回null

protected JsonDeserializer> _findCustomEnumDeserializer(Class>type,

DeserializationConfig config, BeanDescription beanDesc)throwsJsonMappingException

{for(Deserializers d : _factoryConfig.deserializers()) {

JsonDeserializer> deser =d.findEnumDeserializer(type, config, beanDesc);if (deser != null) {returndeser;

}

}return null;

}

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值