以下面的代码为例:
@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为枚举的默认反序列化类了。