Skip to content

Commit e5ab67b

Browse files
committed
ResourceHandlerRegistration exposes List<Resource> locations
1 parent 2d53570 commit e5ab67b

File tree

2 files changed

+85
-72
lines changed

2 files changed

+85
-72
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,6 +42,8 @@ public class ResourceHandlerRegistration {
4242

4343
private final List<String> locationValues = new ArrayList<>();
4444

45+
private final List<Resource> locationsResources = new ArrayList<>();
46+
4547
@Nullable
4648
private Integer cachePeriod;
4749

@@ -82,8 +84,21 @@ public ResourceHandlerRegistration(String... pathPatterns) {
8284
* @return the same {@link ResourceHandlerRegistration} instance, for
8385
* chained method invocation
8486
*/
85-
public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
86-
this.locationValues.addAll(Arrays.asList(resourceLocations));
87+
public ResourceHandlerRegistration addResourceLocations(String... locations) {
88+
this.locationValues.addAll(Arrays.asList(locations));
89+
return this;
90+
}
91+
92+
/**
93+
* Configure locations to serve static resources from based on pre-resolved
94+
* {@code Resource} references.
95+
* @param locations the resource locations to use
96+
* @return the same {@link ResourceHandlerRegistration} instance, for
97+
* chained method invocation
98+
* @since 5.3.3
99+
*/
100+
public ResourceHandlerRegistration addResourceLocations(Resource... locations) {
101+
this.locationsResources.addAll(Arrays.asList(locations));
87102
return this;
88103
}
89104

@@ -181,6 +196,7 @@ protected ResourceHttpRequestHandler getRequestHandler() {
181196
handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
182197
}
183198
handler.setLocationValues(this.locationValues);
199+
handler.setLocations(this.locationsResources);
184200
if (this.cacheControl != null) {
185201
handler.setCacheControl(this.cacheControl);
186202
}

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java

Lines changed: 66 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,12 +21,10 @@
2121
import java.net.URLDecoder;
2222
import java.nio.charset.Charset;
2323
import java.util.ArrayList;
24-
import java.util.Collections;
2524
import java.util.HashMap;
2625
import java.util.List;
2726
import java.util.Locale;
2827
import java.util.Map;
29-
import java.util.stream.Collectors;
3028

3129
import javax.servlet.ServletException;
3230
import javax.servlet.http.HttpServletRequest;
@@ -69,9 +67,10 @@
6967
* {@code HttpRequestHandler} that serves static resources in an optimized way
7068
* according to the guidelines of Page Speed, YSlow, etc.
7169
*
72-
* <p>The {@linkplain #setLocations "locations"} property takes a list of Spring
73-
* {@link Resource} locations from which static resources are allowed to be served
74-
* by this handler. Resources could be served from a classpath location, e.g.
70+
* <p>The properties {@linkplain #setLocations "locations"} and
71+
* {@linkplain #setLocationValues "locationValues"} accept locations from which
72+
* static resources can be served by this handler. This can be relative to the
73+
* root of the web application, or from the classpath, e.g.
7574
* "classpath:/META-INF/public-web-resources/", allowing convenient packaging
7675
* and serving of resources such as .js, .css, and others in jar files.
7776
*
@@ -106,7 +105,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
106105

107106
private final List<String> locationValues = new ArrayList<>(4);
108107

109-
private final List<Resource> locations = new ArrayList<>(4);
108+
private final List<Resource> locationResources = new ArrayList<>(4);
109+
110+
private final List<Resource> locationsToUse = new ArrayList<>(4);
110111

111112
private final Map<Resource, Charset> locationCharsets = new HashMap<>(4);
112113

@@ -149,40 +150,52 @@ public ResourceHttpRequestHandler() {
149150

150151

151152
/**
152-
* An alternative to {@link #setLocations(List)} that accepts a list of
153-
* String-based location values, with support for {@link UrlResource}'s
154-
* (e.g. files or HTTP URLs) with a special prefix to indicate the charset
155-
* to use when appending relative paths. For example
153+
* Configure String-based locations to serve resources from.
154+
* <p>For example, {{@code "/"}, {@code "classpath:/META-INF/public-web-resources/"}}
155+
* allows resources to be served both from the web application root and
156+
* from any JAR on the classpath that contains a
157+
* {@code /META-INF/public-web-resources/} directory, with resources in the
158+
* web application root taking precedence.
159+
* <p>For {@link org.springframework.core.io.UrlResource URL-based resources}
160+
* (e.g. files, HTTP URLs, etc) this method supports a special prefix to
161+
* indicate the charset associated with the URL so that relative paths
162+
* appended to it can be encoded correctly, for example
156163
* {@code "[charset=Windows-31J]https://example.org/path"}.
157164
* @since 4.3.13
165+
* @see #setLocations(List)
158166
*/
159-
public void setLocationValues(List<String> locationValues) {
160-
Assert.notNull(locationValues, "Location values list must not be null");
167+
public void setLocationValues(List<String> locations) {
168+
Assert.notNull(locations, "Locations list must not be null");
161169
this.locationValues.clear();
162-
this.locationValues.addAll(locationValues);
170+
this.locationValues.addAll(locations);
163171
}
164172

165173
/**
166-
* Set the {@code List} of {@code Resource} locations to use as sources
167-
* for serving static resources.
174+
* Configure locations to serve resources from as pre-resourced Resource's.
168175
* @see #setLocationValues(List)
169176
*/
170177
public void setLocations(List<Resource> locations) {
171178
Assert.notNull(locations, "Locations list must not be null");
172-
this.locations.clear();
173-
this.locations.addAll(locations);
179+
this.locationResources.clear();
180+
this.locationResources.addAll(locations);
174181
}
175182

176183
/**
177-
* Return the configured {@code List} of {@code Resource} locations.
178-
* <p>Note that if {@link #setLocationValues(List) locationValues} are provided,
179-
* instead of loaded Resource-based locations, this method will return
180-
* empty until after initialization via {@link #afterPropertiesSet()}.
184+
* Return the configured {@code List} of {@code Resource} locations including
185+
* both String-based locations provided via
186+
* {@link #setLocationValues(List) setLocationValues} and pre-resolved {@code Resource}
187+
* locations provided via {@link #setLocations(List) setLocations}.
188+
* <p>Note that the returned list is fully initialized only after
189+
* initialization via {@link #afterPropertiesSet()}.
181190
* @see #setLocationValues
182191
* @see #setLocations
183192
*/
184193
public List<Resource> getLocations() {
185-
return this.locations;
194+
if (this.locationsToUse.isEmpty()) {
195+
// Possibly not yet initialized, return only what we have so far
196+
return this.locationResources;
197+
}
198+
return this.locationsToUse;
186199
}
187200

188201
/**
@@ -374,7 +387,7 @@ public void setUseLastModified(boolean useLastModified) {
374387
public void afterPropertiesSet() throws Exception {
375388
resolveResourceLocations();
376389

377-
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) {
390+
if (logger.isWarnEnabled() && CollectionUtils.isEmpty(getLocations())) {
378391
logger.warn("Locations list is empty. No resources will be served unless a " +
379392
"custom ResourceResolver is configured as an alternative to PathResourceResolver.");
380393
}
@@ -410,43 +423,38 @@ public void afterPropertiesSet() throws Exception {
410423
}
411424

412425
private void resolveResourceLocations() {
413-
if (CollectionUtils.isEmpty(this.locationValues)) {
414-
return;
415-
}
416-
else if (!CollectionUtils.isEmpty(this.locations)) {
417-
throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " +
418-
"String-based \"locationValues\", but not both.");
419-
}
420-
421-
ApplicationContext applicationContext = obtainApplicationContext();
422-
for (String location : this.locationValues) {
423-
if (this.embeddedValueResolver != null) {
424-
String resolvedLocation = this.embeddedValueResolver.resolveStringValue(location);
425-
if (resolvedLocation == null) {
426-
throw new IllegalArgumentException("Location resolved to null: " + location);
426+
if (!this.locationValues.isEmpty()) {
427+
ApplicationContext applicationContext = obtainApplicationContext();
428+
for (String location : this.locationValues) {
429+
if (this.embeddedValueResolver != null) {
430+
String resolvedLocation = this.embeddedValueResolver.resolveStringValue(location);
431+
if (resolvedLocation == null) {
432+
throw new IllegalArgumentException("Location resolved to null: " + location);
433+
}
434+
location = resolvedLocation;
427435
}
428-
location = resolvedLocation;
429-
}
430-
Charset charset = null;
431-
location = location.trim();
432-
if (location.startsWith(URL_RESOURCE_CHARSET_PREFIX)) {
433-
int endIndex = location.indexOf(']', URL_RESOURCE_CHARSET_PREFIX.length());
434-
if (endIndex == -1) {
435-
throw new IllegalArgumentException("Invalid charset syntax in location: " + location);
436+
Charset charset = null;
437+
location = location.trim();
438+
if (location.startsWith(URL_RESOURCE_CHARSET_PREFIX)) {
439+
int endIndex = location.indexOf(']', URL_RESOURCE_CHARSET_PREFIX.length());
440+
if (endIndex == -1) {
441+
throw new IllegalArgumentException("Invalid charset syntax in location: " + location);
442+
}
443+
String value = location.substring(URL_RESOURCE_CHARSET_PREFIX.length(), endIndex);
444+
charset = Charset.forName(value);
445+
location = location.substring(endIndex + 1);
436446
}
437-
String value = location.substring(URL_RESOURCE_CHARSET_PREFIX.length(), endIndex);
438-
charset = Charset.forName(value);
439-
location = location.substring(endIndex + 1);
440-
}
441-
Resource resource = applicationContext.getResource(location);
442-
this.locations.add(resource);
443-
if (charset != null) {
444-
if (!(resource instanceof UrlResource)) {
445-
throw new IllegalArgumentException("Unexpected charset for non-UrlResource: " + resource);
447+
Resource resource = applicationContext.getResource(location);
448+
this.locationsToUse.add(resource);
449+
if (charset != null) {
450+
if (!(resource instanceof UrlResource)) {
451+
throw new IllegalArgumentException("Unexpected charset for non-UrlResource: " + resource);
452+
}
453+
this.locationCharsets.put(resource, charset);
446454
}
447-
this.locationCharsets.put(resource, charset);
448455
}
449456
}
457+
this.locationsToUse.addAll(this.locationResources);
450458
}
451459

452460
/**
@@ -455,7 +463,7 @@ else if (!CollectionUtils.isEmpty(this.locations)) {
455463
* match the {@link #setLocations locations} configured on this class.
456464
*/
457465
protected void initAllowedLocations() {
458-
if (CollectionUtils.isEmpty(this.locations)) {
466+
if (CollectionUtils.isEmpty(getLocations())) {
459467
return;
460468
}
461469
for (int i = getResourceResolvers().size() - 1; i >= 0; i--) {
@@ -785,17 +793,6 @@ protected void setHeaders(HttpServletResponse response, Resource resource, @Null
785793

786794
@Override
787795
public String toString() {
788-
return "ResourceHttpRequestHandler " + formatLocations();
796+
return "ResourceHttpRequestHandler " + getLocations();
789797
}
790-
791-
private Object formatLocations() {
792-
if (!this.locationValues.isEmpty()) {
793-
return this.locationValues.stream().collect(Collectors.joining("\", \"", "[\"", "\"]"));
794-
}
795-
else if (!this.locations.isEmpty()) {
796-
return this.locations;
797-
}
798-
return Collections.emptyList();
799-
}
800-
801798
}

0 commit comments

Comments
 (0)