From 97e96895dbccf191538ac2b3a5b1ec4e8d2bbd99 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 | 81 +++++++++++-------- .../org/springframework/http/MediaType.java | 2 +- 2 files changed, 47 insertions(+), 36 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 7e02f5528da..c6e6c22ac76 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -140,7 +140,26 @@ 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; + } + + /** + * Create a new {@code MimeType} for the given type, subtype, and parameters. + * @param type the primary type + * @param subtype the subtype + * @param parameters the parameters (may be {@code null}) + * @throws IllegalArgumentException if any of the parameters contains illegal characters + */ + public MimeType(String type, String subtype, @Nullable Map parameters) { + Assert.hasLength(type, "'type' must not be empty"); + Assert.hasLength(subtype, "'subtype' must not be empty"); + checkToken(type); + checkToken(subtype); + this.type = type.toLowerCase(Locale.ROOT); + this.subtype = subtype.toLowerCase(Locale.ROOT); + this.parameters = createParametersMap(parameters); + if (this.parameters.containsKey(PARAM_CHARSET)) { + this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET))); + } } /** @@ -152,7 +171,12 @@ public class MimeType implements Comparable, Serializable { * @since 4.3 */ public MimeType(MimeType other, Charset charset) { - this(other.getType(), other.getSubtype(), addCharsetParameter(charset, other.getParameters())); + 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; } @@ -164,33 +188,11 @@ public class MimeType implements Comparable, Serializable { * @throws IllegalArgumentException if any of the parameters contains illegal characters */ public MimeType(MimeType other, @Nullable Map parameters) { - this(other.getType(), other.getSubtype(), parameters); - } - - /** - * Create a new {@code MimeType} for the given type, subtype, and parameters. - * @param type the primary type - * @param subtype the subtype - * @param parameters the parameters (may be {@code null}) - * @throws IllegalArgumentException if any of the parameters contains illegal characters - */ - public MimeType(String type, String subtype, @Nullable Map parameters) { - Assert.hasLength(type, "'type' must not be empty"); - Assert.hasLength(subtype, "'subtype' must not be empty"); - checkToken(type); - 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); - } - else { - this.parameters = Collections.emptyMap(); + 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))); } } @@ -223,16 +225,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 0ae9fb797dc..1d7ecf7fff9 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -458,7 +458,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); } /**