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:
parent
eb4a9d87fd
commit
9dd3fb70e2
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1900,10 +1900,11 @@ can be achieved as follows:
|
|||
----
|
||||
|
||||
You can also customize the static resource locations using
|
||||
`spring.resources.static-locations` (replacing the default values with a list of directory
|
||||
locations). If you do this the default welcome page detection will switch to your custom
|
||||
locations, so if there is an `index.html` in any of your locations on startup, it will be
|
||||
the home page of the application.
|
||||
`spring.resources.static-locations` (replacing the default values with a list
|
||||
of directory locations). The root Servlet context path `"/"` will be automatically
|
||||
added as a location as well. If you do this the default welcome page detection will
|
||||
switch to your custom locations, so if there is an `index.html` in any of your locations
|
||||
on startup, it will be the home page of the application.
|
||||
|
||||
In addition to the '`standard`' static resource locations above, a special case is made
|
||||
for http://www.webjars.org/[Webjars content]. Any resources with a path in `+/webjars/**+`
|
||||
|
|
@ -2249,7 +2250,7 @@ Unlike Spring MVC, it does not require the Servlet API, is fully asynchronous an
|
|||
non-blocking, and implements the http://www.reactive-streams.org/[Reactive Streams]
|
||||
specification through http://projectreactor.io/[the Reactor project].
|
||||
|
||||
Spring WebFlux comes in two flavors — the annotation-based one is quite close to the
|
||||
Spring WebFlux comes in two flavors; the annotation-based one is quite close to the
|
||||
Spring MVC model we know:
|
||||
|
||||
[source,java,indent=0]
|
||||
|
|
@ -3066,7 +3067,7 @@ auto-configured. In this example it's pulled in transitively via
|
|||
`spring-boot-starter-data-jpa`.
|
||||
|
||||
TIP: If, for whatever reason, you do configure the connection URL for an embedded
|
||||
database, care should be taken to ensure that the database’s automatic shutdown is
|
||||
database, care should be taken to ensure that the database's automatic shutdown is
|
||||
disabled. If you're using H2 you should use `DB_CLOSE_ON_EXIT=FALSE` to do so. If you're
|
||||
using HSQLDB, you should ensure that `shutdown=true` is not used. Disabling the database's
|
||||
automatic shutdown allows Spring Boot to control when the database is closed, thereby
|
||||
|
|
@ -4573,7 +4574,7 @@ recommend to keep this setting enabled if you create your own `RedisCacheManager
|
|||
|
||||
[[boot-features-caching-provider-caffeine]]
|
||||
==== Caffeine
|
||||
Caffeine is a Java 8 rewrite of Guava’s cache that supersede the Guava support. If
|
||||
Caffeine is a Java 8 rewrite of Guava's cache that supersede the Guava support. If
|
||||
Caffeine is present, a `CaffeineCacheManager` (provided by the
|
||||
`spring-boot-starter-cache` '`Starter`') is auto-configured. Caches can be created
|
||||
on startup using the `spring.cache.cache-names` property and customized by one of the
|
||||
|
|
|
|||
Loading…
Reference in New Issue