Remove Servlet-specific static locations

This commit removes the Servlet root context from the default values for
the `spring.resources.static-locations` configuration property. Servlet
and non-Servlet applications are sharing this property.

The Servlet root context is automatically configured as a resource
location for Spring MVC based applications.

Closes gh-9240
This commit is contained in:
Brian Clozel
2017-09-13 15:18:33 +02:00
parent eb4a9d87fd
commit 9dd3fb70e2
5 changed files with 79 additions and 89 deletions

View File

@@ -16,15 +16,7 @@
package org.springframework.boot.autoconfigure.web;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* Properties used to configure resource handling.
@@ -36,30 +28,17 @@ import org.springframework.core.io.ResourceLoader;
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" };
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };
private static final String[] RESOURCE_LOCATIONS;
static {
RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length
+ SERVLET_RESOURCE_LOCATIONS.length];
System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0,
SERVLET_RESOURCE_LOCATIONS.length);
System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS,
SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length);
}
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/] plus context:/ (the root of the servlet context).
* /resources/, /static/, /public/].
*/
private String[] staticLocations = RESOURCE_LOCATIONS;
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
/**
* Cache period for the resources served by the resource handler, in seconds.
@@ -73,12 +52,6 @@ public class ResourceProperties implements ResourceLoaderAware {
private final Chain chain = new Chain();
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public String[] getStaticLocations() {
return this.staticLocations;
@@ -97,45 +70,6 @@ public class ResourceProperties implements ResourceLoaderAware {
return normalized;
}
public Resource getWelcomePage() {
for (String location : getStaticWelcomePageLocations()) {
Resource resource = this.resourceLoader.getResource(location);
try {
if (resource.exists()) {
resource.getURL();
return resource;
}
}
catch (Exception ex) {
// Ignore
}
}
return null;
}
private String[] getStaticWelcomePageLocations() {
String[] result = new String[this.staticLocations.length];
for (int i = 0; i < result.length; i++) {
String location = this.staticLocations[i];
if (!location.endsWith("/")) {
location = location + "/";
}
result[i] = location + "index.html";
}
return result;
}
public List<Resource> resolveFaviconLocations() {
List<Resource> locations = new ArrayList<>(this.staticLocations.length + 1);
if (this.resourceLoader != null) {
for (String location : this.staticLocations) {
locations.add(this.resourceLoader.getResource(location));
}
}
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
public Integer getCachePeriod() {
return this.cachePeriod;
}

View File

@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.web.servlet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -23,6 +25,7 @@ import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
@@ -54,6 +57,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.servlet.filter.OrderedHttpPutFormContentFilter;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -62,7 +66,9 @@ import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
@@ -140,6 +146,8 @@ public class WebMvcAutoConfiguration {
public static final String DEFAULT_SUFFIX = "";
private static final String[] SERVLET_LOCATIONS = { "/" };
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
@@ -158,7 +166,7 @@ public class WebMvcAutoConfiguration {
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
@@ -172,6 +180,8 @@ public class WebMvcAutoConfiguration {
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
private ResourceLoader resourceLoader;
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
@Lazy HttpMessageConverters messageConverters,
@@ -184,6 +194,11 @@ public class WebMvcAutoConfiguration {
.getIfAvailable();
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
@@ -302,18 +317,43 @@ public class WebMvcAutoConfiguration {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(cachePeriod));
}
}
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ResourceProperties resourceProperties) {
return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
public WelcomePageHandlerMapping welcomePageHandlerMapping() {
return new WelcomePageHandlerMapping(getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}
static String[] getResourceLocations(String[] staticLocations) {
String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
System.arraycopy(SERVLET_LOCATIONS, 0, locations,
staticLocations.length, SERVLET_LOCATIONS.length);
return locations;
}
private Optional<Resource> getWelcomePage() {
return Arrays.stream(getResourceLocations(this.resourceProperties.getStaticLocations()))
.map(location -> this.resourceLoader.getResource(location + "index.html"))
.filter(resource -> {
try {
if (resource.exists()) {
resource.getURL();
return true;
}
}
catch (Exception ex) {
// Ignore
}
return false;
})
.findFirst();
}
private void customizeResourceHandlerRegistration(
ResourceHandlerRegistration registration) {
if (this.resourceHandlerRegistrationCustomizer != null) {
@@ -331,14 +371,21 @@ public class WebMvcAutoConfiguration {
@Configuration
@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
public static class FaviconConfiguration {
public static class FaviconConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private ResourceLoader resourceLoader;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
@@ -351,11 +398,19 @@ public class WebMvcAutoConfiguration {
@Bean
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler
.setLocations(this.resourceProperties.resolveFaviconLocations());
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
String[] resourceLocations = getResourceLocations(this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList<>(resourceLocations.length + 1);
Arrays.stream(resourceLocations)
.forEach(location -> locations.add(this.resourceLoader.getResource(location)));
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}
}
@@ -546,9 +601,9 @@ public class WebMvcAutoConfiguration {
private static final Log logger = LogFactory
.getLog(WelcomePageHandlerMapping.class);
private WelcomePageHandlerMapping(Resource welcomePage,
private WelcomePageHandlerMapping(Optional<Resource> welcomePage,
String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName("forward:index.html");

View File

@@ -127,7 +127,7 @@ public class WebFluxAutoConfigurationTests {
SimpleUrlHandlerMapping.class);
assertThat(hm.getUrlMap().get("/**")).isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap().get("/**");
assertThat(staticHandler.getLocations()).hasSize(5);
assertThat(staticHandler.getLocations()).hasSize(4);
assertThat(hm.getUrlMap().get("/webjars/**"))
.isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler webjarsHandler = (ResourceWebHandler) hm.getUrlMap()
@@ -146,7 +146,7 @@ public class WebFluxAutoConfigurationTests {
.isInstanceOf(ResourceWebHandler.class);
ResourceWebHandler staticHandler = (ResourceWebHandler) hm.getUrlMap()
.get("/static/**");
assertThat(staticHandler.getLocations()).hasSize(5);
assertThat(staticHandler.getLocations()).hasSize(4);
}
@Test

View File

@@ -442,7 +442,7 @@ public class WebMvcAutoConfigurationTests {
.withPropertyValues("spring.resources.static-locations=classpath:/static")
.run((context) -> assertThat(
getFaviconMappingLocations(context).get("/**/favicon.ico"))
.hasSize(2));
.hasSize(3));
}
@Test