3232import org .apache .commons .logging .LogFactory ;
3333
3434import org .springframework .beans .factory .InitializingBean ;
35- import org .springframework .beans .factory .config .ConfigurableBeanFactory ;
3635import org .springframework .context .ApplicationContext ;
36+ import org .springframework .context .EmbeddedValueResolverAware ;
3737import org .springframework .core .io .Resource ;
3838import org .springframework .core .io .UrlResource ;
3939import org .springframework .core .io .support .ResourceRegion ;
5151import org .springframework .util .ObjectUtils ;
5252import org .springframework .util .ResourceUtils ;
5353import org .springframework .util .StringUtils ;
54+ import org .springframework .util .StringValueResolver ;
5455import org .springframework .web .HttpRequestHandler ;
5556import org .springframework .web .accept .ContentNegotiationManager ;
5657import org .springframework .web .accept .PathExtensionContentNegotiationStrategy ;
9596 * @since 3.0.4
9697 */
9798public class ResourceHttpRequestHandler extends WebContentGenerator
98- implements HttpRequestHandler , InitializingBean , CorsConfigurationSource {
99+ implements HttpRequestHandler , EmbeddedValueResolverAware , InitializingBean , CorsConfigurationSource {
99100
100101 // Servlet 3.1 setContentLengthLong(long) available?
101102 private static final boolean contentLengthLongAvailable =
@@ -106,12 +107,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
106107 private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset=" ;
107108
108109
110+ private final List <String > locationValues = new ArrayList <String >(4 );
111+
109112 private final List <Resource > locations = new ArrayList <Resource >(4 );
110113
111114 private final Map <Resource , Charset > locationCharsets = new HashMap <Resource , Charset >(4 );
112115
113- private final List <String > locationValues = new ArrayList <String >(4 );
114-
115116 private final List <ResourceResolver > resourceResolvers = new ArrayList <ResourceResolver >(4 );
116117
117118 private final List <ResourceTransformer > resourceTransformers = new ArrayList <ResourceTransformer >(4 );
@@ -128,12 +129,28 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
128129
129130 private UrlPathHelper urlPathHelper ;
130131
132+ private StringValueResolver embeddedValueResolver ;
133+
131134
132135 public ResourceHttpRequestHandler () {
133136 super (HttpMethod .GET .name (), HttpMethod .HEAD .name ());
134137 }
135138
136139
140+ /**
141+ * An alternative to {@link #setLocations(List)} that accepts a list of
142+ * String-based location values, with support for {@link UrlResource}'s
143+ * (e.g. files or HTTP URLs) with a special prefix to indicate the charset
144+ * to use when appending relative paths. For example
145+ * {@code "[charset=Windows-31J]http://example.org/path"}.
146+ * @since 4.3.13
147+ */
148+ public void setLocationValues (List <String > locationValues ) {
149+ Assert .notNull (locationValues , "Location values list must not be null" );
150+ this .locationValues .clear ();
151+ this .locationValues .addAll (locationValues );
152+ }
153+
137154 /**
138155 * Set the {@code List} of {@code Resource} locations to use as sources
139156 * for serving static resources.
@@ -147,28 +164,16 @@ public void setLocations(List<Resource> locations) {
147164
148165 /**
149166 * Return the configured {@code List} of {@code Resource} locations.
150- * Note that if {@link #setLocationValues(List) locationValues} are provided,
167+ * <p> Note that if {@link #setLocationValues(List) locationValues} are provided,
151168 * instead of loaded Resource-based locations, this method will return
152169 * empty until after initialization via {@link #afterPropertiesSet()}.
170+ * @see #setLocationValues
171+ * @see #setLocations
153172 */
154173 public List <Resource > getLocations () {
155174 return this .locations ;
156175 }
157176
158- /**
159- * An alternative to {@link #setLocations(List)} that accepts a list of
160- * String-based location values, with support for {@link UrlResource}'s
161- * (e.g. files or HTTP URLs) with a special prefix to indicate the charset
162- * to use when appending relative paths. For example
163- * {@code "[charset=Windows-31J]http://example.org/path"}.
164- * @since 4.3.13
165- */
166- public void setLocationValues (List <String > locationValues ) {
167- Assert .notNull (locationValues , "Location values list must not be null" );
168- this .locationValues .clear ();
169- this .locationValues .addAll (locationValues );
170- }
171-
172177 /**
173178 * Configure the list of {@link ResourceResolver}s to use.
174179 * <p>By default {@link PathResourceResolver} is configured. If using this property,
@@ -211,8 +216,8 @@ public List<ResourceTransformer> getResourceTransformers() {
211216 * <p>By default a {@link ResourceHttpMessageConverter} will be configured.
212217 * @since 4.3
213218 */
214- public void setResourceHttpMessageConverter (ResourceHttpMessageConverter resourceHttpMessageConverter ) {
215- this .resourceHttpMessageConverter = resourceHttpMessageConverter ;
219+ public void setResourceHttpMessageConverter (ResourceHttpMessageConverter messageConverter ) {
220+ this .resourceHttpMessageConverter = messageConverter ;
216221 }
217222
218223 /**
@@ -228,8 +233,8 @@ public ResourceHttpMessageConverter getResourceHttpMessageConverter() {
228233 * <p>By default a {@link ResourceRegionHttpMessageConverter} will be configured.
229234 * @since 4.3
230235 */
231- public void setResourceRegionHttpMessageConverter (ResourceRegionHttpMessageConverter resourceRegionHttpMessageConverter ) {
232- this .resourceRegionHttpMessageConverter = resourceRegionHttpMessageConverter ;
236+ public void setResourceRegionHttpMessageConverter (ResourceRegionHttpMessageConverter messageConverter ) {
237+ this .resourceRegionHttpMessageConverter = messageConverter ;
233238 }
234239
235240 /**
@@ -244,7 +249,6 @@ public ResourceRegionHttpMessageConverter getResourceRegionHttpMessageConverter(
244249 * Configure a {@code ContentNegotiationManager} to help determine the
245250 * media types for resources being served. If the manager contains a path
246251 * extension strategy it will be checked for registered file extension.
247- * @param contentNegotiationManager the manager in use
248252 * @since 4.3
249253 */
250254 public void setContentNegotiationManager (ContentNegotiationManager contentNegotiationManager ) {
@@ -293,11 +297,15 @@ public UrlPathHelper getUrlPathHelper() {
293297 return this .urlPathHelper ;
294298 }
295299
300+ @ Override
301+ public void setEmbeddedValueResolver (StringValueResolver resolver ) {
302+ this .embeddedValueResolver = resolver ;
303+ }
304+
296305
297306 @ Override
298307 public void afterPropertiesSet () throws Exception {
299-
300- loadResourceLocations ();
308+ resolveResourceLocations ();
301309
302310 if (logger .isWarnEnabled () && CollectionUtils .isEmpty (this .locations )) {
303311 logger .warn ("Locations list is empty. No resources will be served unless a " +
@@ -320,23 +328,23 @@ public void afterPropertiesSet() throws Exception {
320328 this .contentNegotiationStrategy = initContentNegotiationStrategy ();
321329 }
322330
323- private void loadResourceLocations () {
324- if (!CollectionUtils .isEmpty (this .locations ) && !CollectionUtils .isEmpty (this .locationValues )) {
325- throw new IllegalArgumentException ("Please set either Resource-based \" locations\" or " +
326- "String-based \" locationValues\" , but not both." );
327- }
331+ private void resolveResourceLocations () {
328332 if (CollectionUtils .isEmpty (this .locationValues )) {
329333 return ;
330334 }
331- ApplicationContext appContext = getApplicationContext ();
332- ConfigurableBeanFactory beanFactory = null ;
333- if (appContext .getAutowireCapableBeanFactory () instanceof ConfigurableBeanFactory ) {
334- beanFactory = ((ConfigurableBeanFactory ) appContext .getAutowireCapableBeanFactory ());
335+ else if (!CollectionUtils .isEmpty (this .locations )) {
336+ throw new IllegalArgumentException ("Please set either Resource-based \" locations\" or " +
337+ "String-based \" locationValues\" , but not both." );
335338 }
339+
340+ ApplicationContext applicationContext = getApplicationContext ();
336341 for (String location : this .locationValues ) {
337- if (beanFactory != null ) {
338- location = beanFactory .resolveEmbeddedValue (location );
339- Assert .notNull (location , "Null location" );
342+ if (this .embeddedValueResolver != null ) {
343+ String resolvedLocation = this .embeddedValueResolver .resolveStringValue (location );
344+ if (resolvedLocation == null ) {
345+ throw new IllegalArgumentException ("Location resolved to null: " + location );
346+ }
347+ location = resolvedLocation ;
340348 }
341349 Charset charset = null ;
342350 location = location .trim ();
@@ -349,7 +357,7 @@ private void loadResourceLocations() {
349357 charset = Charset .forName (value );
350358 location = location .substring (endIndex + 1 );
351359 }
352- Resource resource = appContext .getResource (location );
360+ Resource resource = applicationContext .getResource (location );
353361 this .locations .add (resource );
354362 if (charset != null ) {
355363 if (!(resource instanceof UrlResource )) {
0 commit comments