diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java index 82bb3432d8c..627fc42418f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ public class ResourceHandlerRegistration { private final String[] pathPatterns; - private final List locationValues = new ArrayList(4); + private final List locationValues = new ArrayList(); private Integer cachePeriod; @@ -56,6 +56,7 @@ public class ResourceHandlerRegistration { this.pathPatterns = pathPatterns; } + /** * Add one or more resource locations from which to serve static content. * Each location must point to a valid directory. Multiple locations may @@ -94,9 +95,7 @@ public class ResourceHandlerRegistration { /** * Specify the {@link org.springframework.http.CacheControl} which should be used * by the resource handler. - * *

Setting a custom value here will override the configuration set with {@link #setCachePeriod}. - * * @param cacheControl the CacheControl configuration to use * @return the same {@link ResourceHandlerRegistration} instance, for chained method invocation * @since 4.2 @@ -109,11 +108,9 @@ public class ResourceHandlerRegistration { /** * Configure a chain of resource resolvers and transformers to use. This * can be useful, for example, to apply a version strategy to resource URLs. - * *

If this method is not invoked, by default only a simple * {@link PathResourceResolver} is used in order to match URL paths to * resources under the configured locations. - * * @param cacheResources whether to cache the result of resource resolution; * setting this to "true" is recommended for production (and "false" for * development, especially when applying a version strategy) @@ -128,11 +125,9 @@ public class ResourceHandlerRegistration { /** * Configure a chain of resource resolvers and transformers to use. This * can be useful, for example, to apply a version strategy to resource URLs. - * *

If this method is not invoked, by default only a simple * {@link PathResourceResolver} is used in order to match URL paths to * resources under the configured locations. - * * @param cacheResources whether to cache the result of resource resolution; * setting this to "true" is recommended for production (and "false" for * development, especially when applying a version strategy @@ -149,15 +144,16 @@ public class ResourceHandlerRegistration { return this.resourceChainRegistration; } + /** - * Returns the URL path patterns for the resource handler. + * Return the URL path patterns for the resource handler. */ protected String[] getPathPatterns() { return this.pathPatterns; } /** - * Returns a {@link ResourceHttpRequestHandler} instance. + * Return a {@link ResourceHttpRequestHandler} instance. */ protected ResourceHttpRequestHandler getRequestHandler() { ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 86706af19f2..f3cec24044e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -32,8 +32,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.ApplicationContext; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.core.io.support.ResourceRegion; @@ -51,6 +51,7 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; +import org.springframework.util.StringValueResolver; import org.springframework.web.HttpRequestHandler; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; @@ -95,7 +96,7 @@ import org.springframework.web.util.UrlPathHelper; * @since 3.0.4 */ public class ResourceHttpRequestHandler extends WebContentGenerator - implements HttpRequestHandler, InitializingBean, CorsConfigurationSource { + implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource { // Servlet 3.1 setContentLengthLong(long) available? private static final boolean contentLengthLongAvailable = @@ -106,12 +107,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator private static final String URL_RESOURCE_CHARSET_PREFIX = "[charset="; + private final List locationValues = new ArrayList(4); + private final List locations = new ArrayList(4); private final Map locationCharsets = new HashMap(4); - private final List locationValues = new ArrayList(4); - private final List resourceResolvers = new ArrayList(4); private final List resourceTransformers = new ArrayList(4); @@ -128,12 +129,28 @@ public class ResourceHttpRequestHandler extends WebContentGenerator private UrlPathHelper urlPathHelper; + private StringValueResolver embeddedValueResolver; + public ResourceHttpRequestHandler() { super(HttpMethod.GET.name(), HttpMethod.HEAD.name()); } + /** + * An alternative to {@link #setLocations(List)} that accepts a list of + * String-based location values, with support for {@link UrlResource}'s + * (e.g. files or HTTP URLs) with a special prefix to indicate the charset + * to use when appending relative paths. For example + * {@code "[charset=Windows-31J]http://example.org/path"}. + * @since 4.3.13 + */ + public void setLocationValues(List locationValues) { + Assert.notNull(locationValues, "Location values list must not be null"); + this.locationValues.clear(); + this.locationValues.addAll(locationValues); + } + /** * Set the {@code List} of {@code Resource} locations to use as sources * for serving static resources. @@ -147,28 +164,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator /** * Return the configured {@code List} of {@code Resource} locations. - * Note that if {@link #setLocationValues(List) locationValues} are provided, + *

Note that if {@link #setLocationValues(List) locationValues} are provided, * instead of loaded Resource-based locations, this method will return * empty until after initialization via {@link #afterPropertiesSet()}. + * @see #setLocationValues + * @see #setLocations */ public List getLocations() { return this.locations; } - /** - * An alternative to {@link #setLocations(List)} that accepts a list of - * String-based location values, with support for {@link UrlResource}'s - * (e.g. files or HTTP URLs) with a special prefix to indicate the charset - * to use when appending relative paths. For example - * {@code "[charset=Windows-31J]http://example.org/path"}. - * @since 4.3.13 - */ - public void setLocationValues(List locationValues) { - Assert.notNull(locationValues, "Location values list must not be null"); - this.locationValues.clear(); - this.locationValues.addAll(locationValues); - } - /** * Configure the list of {@link ResourceResolver}s to use. *

By default {@link PathResourceResolver} is configured. If using this property, @@ -211,8 +216,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator *

By default a {@link ResourceHttpMessageConverter} will be configured. * @since 4.3 */ - public void setResourceHttpMessageConverter(ResourceHttpMessageConverter resourceHttpMessageConverter) { - this.resourceHttpMessageConverter = resourceHttpMessageConverter; + public void setResourceHttpMessageConverter(ResourceHttpMessageConverter messageConverter) { + this.resourceHttpMessageConverter = messageConverter; } /** @@ -228,8 +233,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator *

By default a {@link ResourceRegionHttpMessageConverter} will be configured. * @since 4.3 */ - public void setResourceRegionHttpMessageConverter(ResourceRegionHttpMessageConverter resourceRegionHttpMessageConverter) { - this.resourceRegionHttpMessageConverter = resourceRegionHttpMessageConverter; + public void setResourceRegionHttpMessageConverter(ResourceRegionHttpMessageConverter messageConverter) { + this.resourceRegionHttpMessageConverter = messageConverter; } /** @@ -244,7 +249,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator * Configure a {@code ContentNegotiationManager} to help determine the * media types for resources being served. If the manager contains a path * extension strategy it will be checked for registered file extension. - * @param contentNegotiationManager the manager in use * @since 4.3 */ public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { @@ -293,11 +297,15 @@ public class ResourceHttpRequestHandler extends WebContentGenerator return this.urlPathHelper; } + @Override + public void setEmbeddedValueResolver(StringValueResolver resolver) { + this.embeddedValueResolver = resolver; + } + @Override public void afterPropertiesSet() throws Exception { - - loadResourceLocations(); + resolveResourceLocations(); if (logger.isWarnEnabled() && CollectionUtils.isEmpty(this.locations)) { logger.warn("Locations list is empty. No resources will be served unless a " + @@ -320,23 +328,23 @@ public class ResourceHttpRequestHandler extends WebContentGenerator this.contentNegotiationStrategy = initContentNegotiationStrategy(); } - private void loadResourceLocations() { - if (!CollectionUtils.isEmpty(this.locations) && !CollectionUtils.isEmpty(this.locationValues)) { - throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " + - "String-based \"locationValues\", but not both."); - } + private void resolveResourceLocations() { if (CollectionUtils.isEmpty(this.locationValues)) { return; } - ApplicationContext appContext = getApplicationContext(); - ConfigurableBeanFactory beanFactory = null; - if (appContext.getAutowireCapableBeanFactory() instanceof ConfigurableBeanFactory) { - beanFactory = ((ConfigurableBeanFactory) appContext.getAutowireCapableBeanFactory()); + else if (!CollectionUtils.isEmpty(this.locations)) { + throw new IllegalArgumentException("Please set either Resource-based \"locations\" or " + + "String-based \"locationValues\", but not both."); } + + ApplicationContext applicationContext = getApplicationContext(); for (String location : this.locationValues) { - if (beanFactory != null) { - location = beanFactory.resolveEmbeddedValue(location); - Assert.notNull(location, "Null location"); + if (this.embeddedValueResolver != null) { + String resolvedLocation = this.embeddedValueResolver.resolveStringValue(location); + if (resolvedLocation == null) { + throw new IllegalArgumentException("Location resolved to null: " + location); + } + location = resolvedLocation; } Charset charset = null; location = location.trim(); @@ -349,7 +357,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator charset = Charset.forName(value); location = location.substring(endIndex + 1); } - Resource resource = appContext.getResource(location); + Resource resource = applicationContext.getResource(location); this.locations.add(resource); if (charset != null) { if (!(resource instanceof UrlResource)) {