From 2d81a9fe9ba306042ccfb209f0fdc88393cf303f Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 13 Feb 2026 14:45:47 +0100 Subject: [PATCH] Optimize MediaType(MediaType, Charset) constructor Prior to this commit, the `MediaType` and `MimeType` "copy" constructors would not leverage the fact that the existing instance has been validated already (types, subtype and parameters have been checked already for errors) and the entire validation would be performed again. This would also allocate map instances in the process. This commit ensures that the already validated information is reused directly and that we avoid unnessecary operations and allocations for such constructors. Closes gh-36318 --- .../org/springframework/util/MimeType.java | 91 +++++++++++-------- .../org/springframework/http/MediaType.java | 2 +- 2 files changed, 52 insertions(+), 41 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 281e6328e31..19806be6ff8 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -142,31 +142,6 @@ public class MimeType implements Comparable, Serializable { */ public MimeType(String type, String subtype, Charset charset) { this(type, subtype, Collections.singletonMap(PARAM_CHARSET, charset.name())); - this.resolvedCharset = charset; - } - - /** - * Copy-constructor that copies the type, subtype, parameters of the given {@code MimeType}, - * and allows to set the specified character set. - * @param other the other MimeType - * @param charset the character set - * @throws IllegalArgumentException if any of the parameters contains illegal characters - * @since 4.3 - */ - public MimeType(MimeType other, Charset charset) { - this(other.getType(), other.getSubtype(), addCharsetParameter(charset, other.getParameters())); - this.resolvedCharset = charset; - } - - /** - * Copy-constructor that copies the type and subtype of the given {@code MimeType}, - * and allows for different parameter. - * @param other the other MimeType - * @param parameters the parameters (may be {@code null}) - * @throws IllegalArgumentException if any of the parameters contains illegal characters - */ - public MimeType(MimeType other, @Nullable Map parameters) { - this(other.getType(), other.getSubtype(), parameters); } /** @@ -183,16 +158,43 @@ public class MimeType implements Comparable, Serializable { checkToken(subtype); this.type = type.toLowerCase(Locale.ROOT); this.subtype = subtype.toLowerCase(Locale.ROOT); - if (!CollectionUtils.isEmpty(parameters)) { - Map map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT); - parameters.forEach((parameter, value) -> { - checkParameters(parameter, value); - map.put(parameter, value); - }); - this.parameters = Collections.unmodifiableMap(map); + this.parameters = createParametersMap(parameters); + if (this.parameters.containsKey(PARAM_CHARSET)) { + this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET))); } - else { - this.parameters = Collections.emptyMap(); + } + + /** + * Copy-constructor that copies the type, subtype, parameters of the given {@code MimeType}, + * and allows to set the specified character set. + * @param other the other MimeType + * @param charset the character set + * @throws IllegalArgumentException if any of the parameters contains illegal characters + * @since 4.3 + */ + public MimeType(MimeType other, Charset charset) { + this.type = other.type; + this.subtype = other.subtype; + Map map = new LinkedCaseInsensitiveMap<>(other.parameters.size() + 1, Locale.ROOT); + map.putAll(other.parameters); + map.put(PARAM_CHARSET, charset.name()); + this.parameters = Collections.unmodifiableMap(map); + this.resolvedCharset = charset; + } + + /** + * Copy-constructor that copies the type and subtype of the given {@code MimeType}, + * and allows for different parameter. + * @param other the other MimeType + * @param parameters the parameters (may be {@code null}) + * @throws IllegalArgumentException if any of the parameters contains illegal characters + */ + public MimeType(MimeType other, @Nullable Map parameters) { + this.type = other.type; + this.subtype = other.subtype; + this.parameters = createParametersMap(parameters); + if (this.parameters.containsKey(PARAM_CHARSET)) { + this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET))); } } @@ -225,16 +227,25 @@ public class MimeType implements Comparable, Serializable { } } + private Map createParametersMap(@Nullable Map parameters) { + if (!CollectionUtils.isEmpty(parameters)) { + Map map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT); + parameters.forEach((parameter, value) -> { + checkParameters(parameter, value); + map.put(parameter, value); + }); + return Collections.unmodifiableMap(map); + } + else { + return Collections.emptyMap(); + } + } + protected void checkParameters(String parameter, String value) { Assert.hasLength(parameter, "'parameter' must not be empty"); Assert.hasLength(value, "'value' must not be empty"); checkToken(parameter); - if (PARAM_CHARSET.equals(parameter)) { - if (this.resolvedCharset == null) { - this.resolvedCharset = Charset.forName(unquote(value)); - } - } - else if (!isQuotedString(value)) { + if (!isQuotedString(value)) { checkToken(value); } } 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 e71e1a42727..179a5711ec3 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -549,7 +549,7 @@ public class MediaType extends MimeType implements Serializable { * @throws IllegalArgumentException if any of the parameters contain illegal characters */ public MediaType(MediaType other, @Nullable Map parameters) { - super(other.getType(), other.getSubtype(), parameters); + super(other, parameters); } /**