Update MVC config to use CNM for static resources
The MVC config now plugs the configured ContentNegotiationManager into resource request handling. Issue: SPR-13658
This commit is contained in:
@@ -178,4 +178,23 @@ abstract class MvcNamespaceUtils {
|
||||
return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the {@code ContentNegotiationManager} bean created by or registered
|
||||
* with the {@code annotation-driven} element.
|
||||
* @return a bean definition, bean reference, or null.
|
||||
*/
|
||||
public static Object getContentNegotiationManager(ParserContext context) {
|
||||
String name = AnnotationDrivenBeanDefinitionParser.HANDLER_MAPPING_BEAN_NAME;
|
||||
if (context.getRegistry().containsBeanDefinition(name)) {
|
||||
BeanDefinition handlerMappingBeanDef = context.getRegistry().getBeanDefinition(name);
|
||||
return handlerMappingBeanDef.getPropertyValues().get("contentNegotiationManager");
|
||||
}
|
||||
name = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME;
|
||||
if (context.getRegistry().containsBeanDefinition(name)) {
|
||||
return new RuntimeBeanReference(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -165,17 +166,19 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
RootBeanDefinition resourceHandlerDef = new RootBeanDefinition(ResourceHttpRequestHandler.class);
|
||||
resourceHandlerDef.setSource(source);
|
||||
resourceHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
resourceHandlerDef.getPropertyValues().add("locations", locations);
|
||||
|
||||
MutablePropertyValues values = resourceHandlerDef.getPropertyValues();
|
||||
values.add("locations", locations);
|
||||
|
||||
String cacheSeconds = element.getAttribute("cache-period");
|
||||
if (StringUtils.hasText(cacheSeconds)) {
|
||||
resourceHandlerDef.getPropertyValues().add("cacheSeconds", cacheSeconds);
|
||||
values.add("cacheSeconds", cacheSeconds);
|
||||
}
|
||||
|
||||
Element cacheControlElement = DomUtils.getChildElementByTagName(element, "cache-control");
|
||||
if (cacheControlElement != null) {
|
||||
CacheControl cacheControl = parseCacheControl(cacheControlElement);
|
||||
resourceHandlerDef.getPropertyValues().add("cacheControl", cacheControl);
|
||||
values.add("cacheControl", cacheControl);
|
||||
}
|
||||
|
||||
Element resourceChainElement = DomUtils.getChildElementByTagName(element, "resource-chain");
|
||||
@@ -183,6 +186,11 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
parseResourceChain(resourceHandlerDef, parserContext, resourceChainElement, source);
|
||||
}
|
||||
|
||||
Object manager = MvcNamespaceUtils.getContentNegotiationManager(parserContext);
|
||||
if (manager != null) {
|
||||
values.add("contentNegotiationManager", manager);
|
||||
}
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(resourceHandlerDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, resourceHandlerDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(resourceHandlerDef, beanName));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -22,7 +22,6 @@ import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
@@ -192,24 +191,11 @@ public class ViewResolversBeanDefinitionParser implements BeanDefinitionParser {
|
||||
if (resolverElement.hasAttribute("use-not-acceptable")) {
|
||||
values.add("useNotAcceptableStatusCode", resolverElement.getAttribute("use-not-acceptable"));
|
||||
}
|
||||
Object manager = getContentNegotiationManager(context);
|
||||
Object manager = MvcNamespaceUtils.getContentNegotiationManager(context);
|
||||
if (manager != null) {
|
||||
values.add("contentNegotiationManager", manager);
|
||||
}
|
||||
return beanDef;
|
||||
}
|
||||
|
||||
private Object getContentNegotiationManager(ParserContext context) {
|
||||
String name = AnnotationDrivenBeanDefinitionParser.HANDLER_MAPPING_BEAN_NAME;
|
||||
if (context.getRegistry().containsBeanDefinition(name)) {
|
||||
BeanDefinition handlerMappingBeanDef = context.getRegistry().getBeanDefinition(name);
|
||||
return handlerMappingBeanDef.getPropertyValues().get("contentNegotiationManager");
|
||||
}
|
||||
name = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME;
|
||||
if (context.getRegistry().containsBeanDefinition(name)) {
|
||||
return new RuntimeBeanReference(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
@@ -55,15 +56,23 @@ public class ResourceHandlerRegistry {
|
||||
|
||||
private final ApplicationContext appContext;
|
||||
|
||||
private final ContentNegotiationManager contentNegotiationManager;
|
||||
|
||||
private final List<ResourceHandlerRegistration> registrations = new ArrayList<ResourceHandlerRegistration>();
|
||||
|
||||
private int order = Integer.MAX_VALUE -1;
|
||||
|
||||
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext) {
|
||||
this(applicationContext, servletContext, null);
|
||||
}
|
||||
|
||||
public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletContext servletContext,
|
||||
ContentNegotiationManager contentNegotiationManager) {
|
||||
Assert.notNull(applicationContext, "ApplicationContext is required");
|
||||
this.appContext = applicationContext;
|
||||
this.servletContext = servletContext;
|
||||
this.contentNegotiationManager = contentNegotiationManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +122,7 @@ public class ResourceHandlerRegistry {
|
||||
ResourceHttpRequestHandler handler = registration.getRequestHandler();
|
||||
handler.setServletContext(this.servletContext);
|
||||
handler.setApplicationContext(this.appContext);
|
||||
handler.setContentNegotiationManager(this.contentNegotiationManager);
|
||||
try {
|
||||
handler.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@@ -407,7 +407,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
*/
|
||||
@Bean
|
||||
public HandlerMapping resourceHandlerMapping() {
|
||||
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext);
|
||||
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
|
||||
this.servletContext, mvcContentNegotiationManager());
|
||||
addResourceHandlers(registry);
|
||||
|
||||
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@@ -145,6 +146,8 @@ import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@@ -343,17 +346,21 @@ public class MvcNamespaceTests {
|
||||
|
||||
@Test
|
||||
public void testResources() throws Exception {
|
||||
loadBeanDefinitions("mvc-config-resources.xml", 10);
|
||||
loadBeanDefinitions("mvc-config-resources.xml", 20);
|
||||
|
||||
HttpRequestHandlerAdapter adapter = appContext.getBean(HttpRequestHandlerAdapter.class);
|
||||
assertNotNull(adapter);
|
||||
|
||||
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
|
||||
ContentNegotiationManager manager = mapping.getContentNegotiationManager();
|
||||
|
||||
ResourceHttpRequestHandler handler = appContext.getBean(ResourceHttpRequestHandler.class);
|
||||
assertNotNull(handler);
|
||||
assertSame(manager, handler.getContentNegotiationManager());
|
||||
|
||||
SimpleUrlHandlerMapping mapping = appContext.getBean(SimpleUrlHandlerMapping.class);
|
||||
assertNotNull(mapping);
|
||||
assertEquals(Ordered.LOWEST_PRECEDENCE - 1, mapping.getOrder());
|
||||
SimpleUrlHandlerMapping resourceMapping = appContext.getBean(SimpleUrlHandlerMapping.class);
|
||||
assertNotNull(resourceMapping);
|
||||
assertEquals(Ordered.LOWEST_PRECEDENCE - 1, resourceMapping.getOrder());
|
||||
|
||||
BeanNameUrlHandlerMapping beanNameMapping = appContext.getBean(BeanNameUrlHandlerMapping.class);
|
||||
assertNotNull(beanNameMapping);
|
||||
@@ -362,15 +369,19 @@ public class MvcNamespaceTests {
|
||||
ResourceUrlProvider urlProvider = appContext.getBean(ResourceUrlProvider.class);
|
||||
assertNotNull(urlProvider);
|
||||
|
||||
MappedInterceptor mappedInterceptor = appContext.getBean(MappedInterceptor.class);
|
||||
assertNotNull(urlProvider);
|
||||
assertEquals(ResourceUrlProviderExposingInterceptor.class, mappedInterceptor.getInterceptor().getClass());
|
||||
Map<String, MappedInterceptor> beans = appContext.getBeansOfType(MappedInterceptor.class);
|
||||
List<Class<?>> interceptors = beans.values().stream()
|
||||
.map(mappedInterceptor -> mappedInterceptor.getInterceptor().getClass())
|
||||
.collect(Collectors.toList());
|
||||
assertThat(interceptors, containsInAnyOrder(ConversionServiceExposingInterceptor.class,
|
||||
ResourceUrlProviderExposingInterceptor.class));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setRequestURI("/resources/foo.css");
|
||||
request.setMethod("GET");
|
||||
|
||||
HandlerExecutionChain chain = mapping.getHandler(request);
|
||||
HandlerExecutionChain chain = resourceMapping.getHandler(request);
|
||||
assertNotNull(chain);
|
||||
assertTrue(chain.getHandler() instanceof ResourceHttpRequestHandler);
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
@@ -62,16 +62,19 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
|
||||
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
|
||||
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
|
||||
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
|
||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor;
|
||||
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
|
||||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
@@ -84,6 +87,7 @@ import static com.fasterxml.jackson.databind.MapperFeature.DEFAULT_VIEW_INCLUSIO
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.springframework.http.MediaType.APPLICATION_ATOM_XML;
|
||||
import static org.springframework.http.MediaType.APPLICATION_JSON;
|
||||
import static org.springframework.http.MediaType.APPLICATION_XML;
|
||||
@@ -253,6 +257,15 @@ public class WebMvcConfigurationSupportExtensionTests {
|
||||
request.setRequestURI("/foo");
|
||||
request.setParameter("f", "json");
|
||||
assertEquals(Collections.singletonList(APPLICATION_JSON), manager.resolveMediaTypes(webRequest));
|
||||
|
||||
request.setRequestURI("/resources/foo.gif");
|
||||
SimpleUrlHandlerMapping handlerMapping = (SimpleUrlHandlerMapping) this.config.resourceHandlerMapping();
|
||||
handlerMapping.setApplicationContext(this.context);
|
||||
HandlerExecutionChain chain = handlerMapping.getHandler(request);
|
||||
assertNotNull(chain);
|
||||
ResourceHttpRequestHandler handler = (ResourceHttpRequestHandler) chain.getHandler();
|
||||
assertNotNull(handler);
|
||||
assertSame(manager, handler.getContentNegotiationManager());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
|
||||
|
||||
<mvc:annotation-driven />
|
||||
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/" />
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user