From e77faf748499f2136ac59cdf51fdd10720f550d4 Mon Sep 17 00:00:00 2001 From: James Yuzawa Date: Sat, 25 Mar 2023 12:21:10 -0400 Subject: [PATCH] Improve performance of canRead() in HttpMessageReader's Use MimeType.WILDCARD_TYPE for faster String.equals(). Move cheaper checks to the front of the canRead implementations. See gh-30192 --- .../org/springframework/util/MimeType.java | 5 +++-- .../springframework/util/MimeTypeUtils.java | 2 +- .../org/springframework/http/MediaType.java | 4 ++-- .../http/codec/FormHttpMessageReader.java | 14 +++++++----- .../codec/json/AbstractJackson2Decoder.java | 10 ++++----- .../multipart/MultipartHttpMessageReader.java | 22 ++++++++++--------- 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index a1464d8221..ab37ff8e39 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -268,7 +268,8 @@ public class MimeType implements Comparable, Serializable { * @return whether the subtype is a wildcard */ public boolean isWildcardSubtype() { - return WILDCARD_TYPE.equals(getSubtype()) || getSubtype().startsWith("*+"); + String subtype = getSubtype(); + return WILDCARD_TYPE.equals(subtype) || subtype.startsWith("*+"); } /** diff --git a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java index cc069f6837..27bbc609fc 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java +++ b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java @@ -181,7 +181,7 @@ public abstract class MimeTypeUtils { static { // Not using "parseMimeType" to avoid static init cost - ALL = new MimeType("*", "*"); + ALL = new MimeType(MimeType.WILDCARD_TYPE, MimeType.WILDCARD_TYPE); APPLICATION_GRAPHQL = new MimeType("application", "graphql+json"); APPLICATION_JSON = new MimeType("application", "json"); APPLICATION_OCTET_STREAM = new MimeType("application", "octet-stream"); diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index 149805b879..4760864b24 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -435,7 +435,7 @@ public class MediaType extends MimeType implements Serializable { static { // Not using "valueOf' to avoid static init cost - ALL = new MediaType("*", "*"); + ALL = new MediaType(MimeType.WILDCARD_TYPE, MimeType.WILDCARD_TYPE); APPLICATION_ATOM_XML = new MediaType("application", "atom+xml"); APPLICATION_CBOR = new MediaType("application", "cbor"); APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded"); diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java index c5e61ad369..ae60fc7389 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java @@ -104,12 +104,14 @@ public class FormHttpMessageReader extends LoggingCodecSupport @Override public boolean canRead(ResolvableType elementType, @Nullable MediaType mediaType) { - boolean multiValueUnresolved = - elementType.hasUnresolvableGenerics() && - MultiValueMap.class.isAssignableFrom(elementType.toClass()); - - return ((MULTIVALUE_STRINGS_TYPE.isAssignableFrom(elementType) || multiValueUnresolved) && - (mediaType == null || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType))); + if (mediaType == null || MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)) { + if (MultiValueMap.class.isAssignableFrom(elementType.toClass()) && + elementType.hasUnresolvableGenerics()) { + return true; + } + return MULTIVALUE_STRINGS_TYPE.isAssignableFrom(elementType); + } + return false; } @Override diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java index dc320b50cb..cc2a39ae2e 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,15 +97,15 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple @Override public boolean canDecode(ResolvableType elementType, @Nullable MimeType mimeType) { + // Skip String: CharSequenceDecoder + "*/*" comes after + if (CharSequence.class.isAssignableFrom(elementType.toClass()) || !supportsMimeType(mimeType)) { + return false; + } ObjectMapper mapper = selectObjectMapper(elementType, mimeType); if (mapper == null) { return false; } JavaType javaType = mapper.constructType(elementType.getType()); - // Skip String: CharSequenceDecoder + "*/*" comes after - if (CharSequence.class.isAssignableFrom(elementType.toClass()) || !supportsMimeType(mimeType)) { - return false; - } if (!logger.isDebugEnabled()) { return mapper.canDeserialize(javaType); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java index 74c61f18c9..fa5914f096 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,21 +81,23 @@ public class MultipartHttpMessageReader extends LoggingCodecSupport return MIME_TYPES; } - @Override - public boolean canRead(ResolvableType elementType, @Nullable MediaType mediaType) { - if (MULTIPART_VALUE_TYPE.isAssignableFrom(elementType)) { - if (mediaType == null) { + private boolean supportsMediaType(@Nullable MediaType mediaType) { + if (mediaType == null) { + return true; + } + for (MediaType supportedMediaType : MIME_TYPES) { + if (supportedMediaType.isCompatibleWith(mediaType)) { return true; } - for (MediaType supportedMediaType : MIME_TYPES) { - if (supportedMediaType.isCompatibleWith(mediaType)) { - return true; - } - } } return false; } + @Override + public boolean canRead(ResolvableType elementType, @Nullable MediaType mediaType) { + return supportsMediaType(mediaType) && MULTIPART_VALUE_TYPE.isAssignableFrom(elementType); + } + @Override public Flux> read(ResolvableType elementType,