Skip to content

Commit 97e9689

Browse files
committed
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
1 parent e98c2fe commit 97e9689

2 files changed

Lines changed: 47 additions & 36 deletions

File tree

spring-core/src/main/java/org/springframework/util/MimeType.java

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,26 @@ public MimeType(String type, String subtype) {
140140
*/
141141
public MimeType(String type, String subtype, Charset charset) {
142142
this(type, subtype, Collections.singletonMap(PARAM_CHARSET, charset.name()));
143-
this.resolvedCharset = charset;
143+
}
144+
145+
/**
146+
* Create a new {@code MimeType} for the given type, subtype, and parameters.
147+
* @param type the primary type
148+
* @param subtype the subtype
149+
* @param parameters the parameters (may be {@code null})
150+
* @throws IllegalArgumentException if any of the parameters contains illegal characters
151+
*/
152+
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
153+
Assert.hasLength(type, "'type' must not be empty");
154+
Assert.hasLength(subtype, "'subtype' must not be empty");
155+
checkToken(type);
156+
checkToken(subtype);
157+
this.type = type.toLowerCase(Locale.ROOT);
158+
this.subtype = subtype.toLowerCase(Locale.ROOT);
159+
this.parameters = createParametersMap(parameters);
160+
if (this.parameters.containsKey(PARAM_CHARSET)) {
161+
this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET)));
162+
}
144163
}
145164

146165
/**
@@ -152,7 +171,12 @@ public MimeType(String type, String subtype, Charset charset) {
152171
* @since 4.3
153172
*/
154173
public MimeType(MimeType other, Charset charset) {
155-
this(other.getType(), other.getSubtype(), addCharsetParameter(charset, other.getParameters()));
174+
this.type = other.type;
175+
this.subtype = other.subtype;
176+
Map<String, String> map = new LinkedCaseInsensitiveMap<>(other.parameters.size() + 1, Locale.ROOT);
177+
map.putAll(other.parameters);
178+
map.put(PARAM_CHARSET, charset.name());
179+
this.parameters = Collections.unmodifiableMap(map);
156180
this.resolvedCharset = charset;
157181
}
158182

@@ -164,33 +188,11 @@ public MimeType(MimeType other, Charset charset) {
164188
* @throws IllegalArgumentException if any of the parameters contains illegal characters
165189
*/
166190
public MimeType(MimeType other, @Nullable Map<String, String> parameters) {
167-
this(other.getType(), other.getSubtype(), parameters);
168-
}
169-
170-
/**
171-
* Create a new {@code MimeType} for the given type, subtype, and parameters.
172-
* @param type the primary type
173-
* @param subtype the subtype
174-
* @param parameters the parameters (may be {@code null})
175-
* @throws IllegalArgumentException if any of the parameters contains illegal characters
176-
*/
177-
public MimeType(String type, String subtype, @Nullable Map<String, String> parameters) {
178-
Assert.hasLength(type, "'type' must not be empty");
179-
Assert.hasLength(subtype, "'subtype' must not be empty");
180-
checkToken(type);
181-
checkToken(subtype);
182-
this.type = type.toLowerCase(Locale.ROOT);
183-
this.subtype = subtype.toLowerCase(Locale.ROOT);
184-
if (!CollectionUtils.isEmpty(parameters)) {
185-
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT);
186-
parameters.forEach((parameter, value) -> {
187-
checkParameters(parameter, value);
188-
map.put(parameter, value);
189-
});
190-
this.parameters = Collections.unmodifiableMap(map);
191-
}
192-
else {
193-
this.parameters = Collections.emptyMap();
191+
this.type = other.type;
192+
this.subtype = other.subtype;
193+
this.parameters = createParametersMap(parameters);
194+
if (this.parameters.containsKey(PARAM_CHARSET)) {
195+
this.resolvedCharset = Charset.forName(unquote(this.parameters.get(PARAM_CHARSET)));
194196
}
195197
}
196198

@@ -223,16 +225,25 @@ private void checkToken(String token) {
223225
}
224226
}
225227

228+
private Map<String, String> createParametersMap(@Nullable Map<String, String> parameters) {
229+
if (!CollectionUtils.isEmpty(parameters)) {
230+
Map<String, String> map = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT);
231+
parameters.forEach((parameter, value) -> {
232+
checkParameters(parameter, value);
233+
map.put(parameter, value);
234+
});
235+
return Collections.unmodifiableMap(map);
236+
}
237+
else {
238+
return Collections.emptyMap();
239+
}
240+
}
241+
226242
protected void checkParameters(String parameter, String value) {
227243
Assert.hasLength(parameter, "'parameter' must not be empty");
228244
Assert.hasLength(value, "'value' must not be empty");
229245
checkToken(parameter);
230-
if (PARAM_CHARSET.equals(parameter)) {
231-
if (this.resolvedCharset == null) {
232-
this.resolvedCharset = Charset.forName(unquote(value));
233-
}
234-
}
235-
else if (!isQuotedString(value)) {
246+
if (!isQuotedString(value)) {
236247
checkToken(value);
237248
}
238249
}

spring-web/src/main/java/org/springframework/http/MediaType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ public MediaType(MediaType other, Charset charset) {
458458
* @throws IllegalArgumentException if any of the parameters contain illegal characters
459459
*/
460460
public MediaType(MediaType other, @Nullable Map<String, String> parameters) {
461-
super(other.getType(), other.getSubtype(), parameters);
461+
super(other, parameters);
462462
}
463463

464464
/**

0 commit comments

Comments
 (0)