diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java
index e16bd2d91e3..881204f283b 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java
@@ -20,14 +20,13 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
-import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Enables default Spring MVC configuration and registers Spring MVC infrastructure components expected by the
- * {@link DispatcherServlet}. To use this annotation simply place it on an application @{@link Configuration} class
- * and that will in turn import default Spring MVC configuration including support for annotated methods
- * in @{@link Controller} classes.
+ * {@link DispatcherServlet}. Add this annotation to an application @{@link Configuration} class. It will in
+ * turn import the @{@link Configuration} class {@link WebMvcConfiguration}, which provides default Spring MVC
+ * configuration.
*
* @Configuration
* @EnableWebMvc
@@ -39,11 +38,10 @@ import org.springframework.web.servlet.DispatcherServlet;
*
* }
*
- * To customize the imported configuration you simply implement {@link WebMvcConfigurer}, or more likely extend
- * {@link WebMvcConfigurerAdapter} overriding selected methods only. The most obvious place to do this is
- * the @{@link Configuration} class that enabled the Spring MVC configuration via @{@link EnableWebMvc}.
- * However any @{@link Configuration} class and more generally any Spring bean can implement {@link WebMvcConfigurer}
- * to be detected and given an opportunity to customize Spring MVC configuration at startup.
+ *
To customize the imported configuration implement {@link WebMvcConfigurer}, or more conveniently extend
+ * {@link WebMvcConfigurerAdapter} overriding specific methods. Your @{@link Configuration} class and any other
+ * Spring bean that implements {@link WebMvcConfigurer} will be detected and given an opportunity to customize
+ * the default Spring MVC configuration through the callback methods on the {@link WebMvcConfigurer} interface.
*
* @Configuration
* @EnableWebMvc
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java
index 103770131dc..3ce466121e1 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurer.java
@@ -22,77 +22,74 @@ import java.util.List;
import org.springframework.util.Assert;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
-import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.MappedInterceptor;
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
/**
* Helps with configuring an ordered set of Spring MVC interceptors of type {@link HandlerInterceptor} or
- * {@link WebRequestInterceptor}. Registered interceptors will generally be detected by all {@link HandlerMapping}
- * instances in a Spring MVC web application context. Interceptors can be added with a set of path patterns to
- * which they should apply.
+ * {@link WebRequestInterceptor}. Interceptors can be registered with a set of path patterns.
*
* @author Rossen Stoyanchev
* @since 3.1
*/
public class InterceptorConfigurer {
- private final List mappedInterceptors = new ArrayList();
+ private final List interceptors = new ArrayList();
/**
* Add a {@link HandlerInterceptor} that should apply to any request.
*/
public void addInterceptor(HandlerInterceptor interceptor) {
- register(null, interceptor);
+ register(interceptor);
}
/**
* Add a {@link WebRequestInterceptor} that should apply to any request.
*/
public void addInterceptor(WebRequestInterceptor interceptor) {
- register(null, asHandlerInterceptorArray(interceptor));
+ register(asHandlerInterceptorArray(interceptor));
}
/**
* Add {@link HandlerInterceptor}s that should apply to any request.
*/
public void addInterceptors(HandlerInterceptor... interceptors) {
- register(null, interceptors);
+ register( interceptors);
}
/**
* Add {@link WebRequestInterceptor}s that should apply to any request.
*/
public void addInterceptors(WebRequestInterceptor... interceptors) {
- register(null, asHandlerInterceptorArray(interceptors));
+ register(asHandlerInterceptorArray(interceptors));
}
/**
* Add a {@link HandlerInterceptor} with a set of URL path patterns it should apply to.
*/
public void mapInterceptor(String[] pathPatterns, HandlerInterceptor interceptor) {
- register(pathPatterns, interceptor);
+ registerMappedInterceptors(pathPatterns, interceptor);
}
/**
* Add a {@link WebRequestInterceptor} with a set of URL path patterns it should apply to.
*/
public void mapInterceptor(String[] pathPatterns, WebRequestInterceptor interceptors) {
- register(pathPatterns, asHandlerInterceptorArray(interceptors));
+ registerMappedInterceptors(pathPatterns, asHandlerInterceptorArray(interceptors));
}
/**
* Add {@link HandlerInterceptor}s with a set of URL path patterns they should apply to.
*/
public void mapInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors) {
- register(pathPatterns, interceptors);
+ registerMappedInterceptors(pathPatterns, interceptors);
}
/**
* Add {@link WebRequestInterceptor}s with a set of URL path patterns they should apply to.
*/
public void mapInterceptors(String[] pathPatterns, WebRequestInterceptor... interceptors) {
- register(pathPatterns, asHandlerInterceptorArray(interceptors));
+ registerMappedInterceptors(pathPatterns, asHandlerInterceptorArray(interceptors));
}
private static HandlerInterceptor[] asHandlerInterceptorArray(WebRequestInterceptor...interceptors) {
@@ -103,23 +100,35 @@ public class InterceptorConfigurer {
return result;
}
+ /**
+ * Stores the given set of {@link HandlerInterceptor}s internally.
+ * @param interceptors one or more interceptors to be stored
+ */
+ protected void register(HandlerInterceptor...interceptors) {
+ Assert.notEmpty(interceptors, "At least one interceptor must be provided");
+ for (HandlerInterceptor interceptor : interceptors) {
+ this.interceptors.add(interceptor);
+ }
+ }
+
/**
* Stores the given set of {@link HandlerInterceptor}s and path patterns internally.
* @param pathPatterns path patterns or {@code null}
* @param interceptors one or more interceptors to be stored
*/
- protected void register(String[] pathPatterns, HandlerInterceptor...interceptors) {
+ protected void registerMappedInterceptors(String[] pathPatterns, HandlerInterceptor...interceptors) {
Assert.notEmpty(interceptors, "At least one interceptor must be provided");
+ Assert.notEmpty(pathPatterns, "Path patterns must be provided");
for (HandlerInterceptor interceptor : interceptors) {
- mappedInterceptors.add(new MappedInterceptor(pathPatterns, interceptor));
+ this.interceptors.add(new MappedInterceptor(pathPatterns, interceptor));
}
}
/**
* Returns all registered interceptors.
*/
- protected List getInterceptors() {
- return mappedInterceptors;
+ protected List getInterceptors() {
+ return interceptors;
}
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java
index 6f93cf62cf4..445fb79c629 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java
@@ -19,95 +19,31 @@ package org.springframework.web.servlet.config.annotation;
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.ServletContext;
-import javax.xml.transform.Source;
-
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.format.support.FormattingConversionService;
-import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.ResourceHttpMessageConverter;
-import org.springframework.http.converter.StringHttpMessageConverter;
-import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
-import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
-import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
-import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
-import org.springframework.http.converter.xml.SourceHttpMessageConverter;
-import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
-import org.springframework.util.ClassUtils;
-import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
-import org.springframework.web.HttpRequestHandler;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
-import org.springframework.web.context.ServletContextAware;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.HandlerMapping;
-import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
-import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
-import org.springframework.web.servlet.handler.MappedInterceptor;
-import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
-import org.springframework.web.servlet.mvc.Controller;
-import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
-import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
-import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
-import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
-import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
-import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
/**
- * Provides default configuration for Spring MVC applications. Registers Spring MVC infrastructure components to be
- * detected by the {@link DispatcherServlet}. Further below is a list of registered instances. This configuration is
- * enabled through the {@link EnableWebMvc} annotation.
- *
- * A number of options are available for customizing the default configuration provided by this class.
- * See {@link EnableWebMvc} and {@link WebMvcConfigurer} for details.
- *
- *
Registers these handler mappings:
- *
- * {@link RequestMappingHandlerMapping} ordered at 0 for mapping requests to annotated controller methods.
- * {@link SimpleUrlHandlerMapping} ordered at 1 to map URL paths directly to view names.
- * {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL paths to controller bean names.
- * {@link SimpleUrlHandlerMapping} ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests.
- * {@link SimpleUrlHandlerMapping} ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet.
- *
- *
- * Note: that the SimpleUrlHandlerMapping instances above will have empty URL maps and
- * hence no effect until explicitly configured via one of the {@link WebMvcConfigurer} callbacks.
- *
- *
Registers these handler adapters:
- *
- * {@link RequestMappingHandlerAdapter} for processing requests using annotated controller methods.
- * {@link HttpRequestHandlerAdapter} for processing requests with {@link HttpRequestHandler}s.
- * {@link SimpleControllerHandlerAdapter} for processing requests with interface-based {@link Controller}s.
- *
- *
- * Registers a {@link HandlerExceptionResolverComposite} with this chain of exception resolvers:
- *
- * {@link ExceptionHandlerExceptionResolver} for handling exceptions through @{@link ExceptionHandler} methods.
- * {@link ResponseStatusExceptionResolver} for exceptions annotated with @{@link ResponseStatus}.
- * {@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
- *
- *
- * Registers the following others:
- *
- * {@link FormattingConversionService} for use with annotated controller methods and the spring:eval JSP tag.
- * {@link Validator} for validating model attributes on annotated controller methods.
- *
+ * Provides default configuration for Spring MVC applications by registering Spring MVC infrastructure components
+ * to be detected by the {@link DispatcherServlet}. This class is imported whenever @{@link EnableWebMvc} is
+ * added to an @{@link Configuration} class.
+ *
+ * See the base class {@link WebMvcConfigurationSupport} for a list of registered instances. This class is closed
+ * for extension. However, the configuration it provides can be customized by having your @{@link Configuration}
+ * class implement {@link WebMvcConfigurer} or more conveniently extend from {@link WebMvcConfigurerAdapter}.
+ *
+ *
This class will detect your @{@link Configuration} class and any other @{@link Configuration} classes that
+ * implement {@link WebMvcConfigurer} via autowiring and will allow each of them to participate in the process
+ * of configuring Spring MVC through the configuration callbacks defined in {@link WebMvcConfigurer}.
*
* @see EnableWebMvc
* @see WebMvcConfigurer
@@ -116,18 +52,10 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
* @since 3.1
*/
@Configuration
-class WebMvcConfiguration implements ApplicationContextAware, ServletContextAware {
+class WebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
- private ServletContext servletContext;
-
- private ApplicationContext applicationContext;
-
- private List mappedInterceptors;
-
- private List> messageConverters;
-
@Autowired(required = false)
public void setConfigurers(List configurers) {
if (configurers == null || configurers.isEmpty()) {
@@ -136,75 +64,30 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar
this.configurers.addWebMvcConfigurers(configurers);
}
- public void setServletContext(ServletContext servletContext) {
- this.servletContext = servletContext;
+ @Override
+ protected void configureInterceptors(InterceptorConfigurer configurer) {
+ configurers.configureInterceptors(configurer);
}
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- @Bean
- public RequestMappingHandlerMapping requestMappingHandlerMapping() {
- RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
- mapping.setInterceptors(getMappedInterceptors());
- mapping.setOrder(0);
- return mapping;
- }
-
- private Object[] getMappedInterceptors() {
- if (mappedInterceptors == null) {
- InterceptorConfigurer configurer = new InterceptorConfigurer();
- configurers.configureInterceptors(configurer);
- configurer.addInterceptor(new ConversionServiceExposingInterceptor(conversionService()));
- mappedInterceptors = configurer.getInterceptors();
- }
- return mappedInterceptors.toArray();
- }
-
- @Bean
- public HandlerMapping viewControllerHandlerMapping() {
- ViewControllerConfigurer configurer = new ViewControllerConfigurer();
- configurer.setOrder(1);
+ @Override
+ protected void configureViewControllers(ViewControllerConfigurer configurer) {
configurers.configureViewControllers(configurer);
-
- SimpleUrlHandlerMapping handlerMapping = configurer.getHandlerMapping();
- handlerMapping.setInterceptors(getMappedInterceptors());
- return handlerMapping;
- }
-
- @Bean
- public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
- BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
- mapping.setOrder(2);
- mapping.setInterceptors(getMappedInterceptors());
- return mapping;
}
- @Bean
- public HandlerMapping resourceHandlerMapping() {
- ResourceConfigurer configurer = new ResourceConfigurer(applicationContext, servletContext);
- configurer.setOrder(Integer.MAX_VALUE-1);
+ @Override
+ protected void configureResourceHandling(ResourceConfigurer configurer) {
configurers.configureResourceHandling(configurer);
- return configurer.getHandlerMapping();
}
- @Bean
- public HandlerMapping defaultServletHandlerMapping() {
- DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
+ @Override
+ protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurers.configureDefaultServletHandling(configurer);
- return configurer.getHandlerMapping();
}
+ @Override
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
- RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
- adapter.setMessageConverters(getMessageConverters());
-
- ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer();
- bindingInitializer.setConversionService(conversionService());
- bindingInitializer.setValidator(validator());
- adapter.setWebBindingInitializer(bindingInitializer);
+ RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
List argumentResolvers = new ArrayList();
configurers.addArgumentResolvers(argumentResolvers);
@@ -213,116 +96,40 @@ class WebMvcConfiguration implements ApplicationContextAware, ServletContextAwar
List returnValueHandlers = new ArrayList();
configurers.addReturnValueHandlers(returnValueHandlers);
adapter.setCustomReturnValueHandlers(returnValueHandlers);
-
+
return adapter;
}
- private List> getMessageConverters() {
- if (messageConverters == null) {
- messageConverters = new ArrayList>();
- configurers.configureMessageConverters(messageConverters);
- if (messageConverters.isEmpty()) {
- addDefaultHttpMessageConverters(messageConverters);
- }
- }
- return messageConverters;
- }
-
- @Bean(name="webMvcConversionService")
- public FormattingConversionService conversionService() {
- FormattingConversionService conversionService = new DefaultFormattingConversionService();
- configurers.addFormatters(conversionService);
- return conversionService;
- }
-
- @Bean(name="webMvcValidator")
- public Validator validator() {
- Validator validator = configurers.getValidator();
- if (validator != null) {
- return validator;
- }
- else if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
- Class> clazz;
- try {
- String className = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean";
- clazz = ClassUtils.forName(className, WebMvcConfiguration.class.getClassLoader());
- } catch (ClassNotFoundException e) {
- throw new BeanInitializationException("Could not find default validator");
- } catch (LinkageError e) {
- throw new BeanInitializationException("Could not find default validator");
- }
- return (Validator) BeanUtils.instantiate(clazz);
- }
- else {
- return NOOP_VALIDATOR;
- }
- }
-
- private void addDefaultHttpMessageConverters(List> messageConverters) {
- StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
- stringConverter.setWriteAcceptCharset(false);
-
- messageConverters.add(new ByteArrayHttpMessageConverter());
- messageConverters.add(stringConverter);
- messageConverters.add(new ResourceHttpMessageConverter());
- messageConverters.add(new SourceHttpMessageConverter());
- messageConverters.add(new XmlAwareFormHttpMessageConverter());
-
- ClassLoader classLoader = getClass().getClassLoader();
- if (ClassUtils.isPresent("javax.xml.bind.Binder", classLoader)) {
- messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
- }
- if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader)) {
- messageConverters.add(new MappingJacksonHttpMessageConverter());
- }
- if (ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", classLoader)) {
- messageConverters.add(new AtomFeedHttpMessageConverter());
- messageConverters.add(new RssChannelHttpMessageConverter());
- }
+ @Override
+ protected void configureMessageConverters(List> converters) {
+ configurers.configureMessageConverters(converters);
}
+ @Override
@Bean
- public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
- return new HttpRequestHandlerAdapter();
+ public FormattingConversionService mvcConversionService() {
+ FormattingConversionService conversionService = super.mvcConversionService();
+ configurers.addFormatters(conversionService);
+ return conversionService;
}
+ @Override
@Bean
- public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
- return new SimpleControllerHandlerAdapter();
+ public Validator mvcValidator() {
+ Validator validator = configurers.getValidator();
+ return validator != null ? validator : super.mvcValidator();
}
@Bean
- public HandlerExceptionResolver handlerExceptionResolver() throws Exception {
+ public HandlerExceptionResolverComposite handlerExceptionResolver() throws Exception {
List resolvers = new ArrayList();
configurers.configureHandlerExceptionResolvers(resolvers);
- if (resolvers.size() == 0) {
- resolvers.add(createExceptionHandlerExceptionResolver());
- resolvers.add(new ResponseStatusExceptionResolver());
- resolvers.add(new DefaultHandlerExceptionResolver());
+ HandlerExceptionResolverComposite composite = super.handlerExceptionResolver();
+ if (resolvers.size() != 0) {
+ composite.setExceptionResolvers(resolvers);
}
-
- HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
- composite.setOrder(0);
- composite.setExceptionResolvers(resolvers);
return composite;
}
-
- private HandlerExceptionResolver createExceptionHandlerExceptionResolver() throws Exception {
- ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
- resolver.setMessageConverters(getMessageConverters());
- resolver.afterPropertiesSet();
- return resolver;
- }
- private static final Validator NOOP_VALIDATOR = new Validator() {
-
- public boolean supports(Class> clazz) {
- return false;
- }
-
- public void validate(Object target, Errors errors) {
- }
- };
-
}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java
new file mode 100644
index 00000000000..99559ba4b4f
--- /dev/null
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2002-2011 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.servlet.config.annotation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.xml.transform.Source;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.support.DefaultFormattingConversionService;
+import org.springframework.format.support.FormattingConversionService;
+import org.springframework.http.converter.ByteArrayHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.ResourceHttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter;
+import org.springframework.http.converter.feed.RssChannelHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter;
+import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
+import org.springframework.http.converter.xml.SourceHttpMessageConverter;
+import org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter;
+import org.springframework.util.ClassUtils;
+import org.springframework.validation.Errors;
+import org.springframework.validation.MessageCodesResolver;
+import org.springframework.validation.Validator;
+import org.springframework.web.HttpRequestHandler;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
+import org.springframework.web.context.ServletContextAware;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.HandlerAdapter;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
+import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
+import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
+import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
+import org.springframework.web.servlet.mvc.Controller;
+import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
+import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
+import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
+import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
+
+/**
+ * A base class that provides default configuration for Spring MVC applications by registering Spring MVC
+ * infrastructure components to be detected by the {@link DispatcherServlet}. Typically applications should not
+ * have to start by extending this class. A much easier place to start is to annotate your @{@link Configuration}
+ * class with @{@link EnableWebMvc}. See @{@link EnableWebMvc} and {@link WebMvcConfigurer}.
+ *
+ * If using @{@link EnableWebMvc} and extending from {@link WebMvcConfigurerAdapter} does not give you the level
+ * of flexibility you need, consider extending directly from this class instead. Remember to add @{@link Configuration}
+ * to you subclass and @{@link Bean} to any @{@link Bean} methods you choose to override. A few example reasons for
+ * extending this class include providing a custom {@link MessageCodesResolver}, changing the order of
+ * {@link HandlerMapping} instances, plugging in a variant of any of the beans provided by this class, and so on.
+ *
+ *
This class registers the following {@link HandlerMapping}s:
+ *
+ * {@link RequestMappingHandlerMapping} ordered at 0 for mapping requests to annotated controller methods.
+ * {@link SimpleUrlHandlerMapping} ordered at 1 to map URL paths directly to view names.
+ * {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL paths to controller bean names.
+ * {@link SimpleUrlHandlerMapping} ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests.
+ * {@link SimpleUrlHandlerMapping} ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet.
+ *
+ *
+ * Registers {@link HandlerAdapter}s:
+ *
+ * {@link RequestMappingHandlerAdapter} for processing requests using annotated controller methods.
+ * {@link HttpRequestHandlerAdapter} for processing requests with {@link HttpRequestHandler}s.
+ * {@link SimpleControllerHandlerAdapter} for processing requests with interface-based {@link Controller}s.
+ *
+ *
+ * Registers a {@link HandlerExceptionResolverComposite} with this chain of exception resolvers:
+ *
+ * {@link ExceptionHandlerExceptionResolver} for handling exceptions through @{@link ExceptionHandler} methods.
+ * {@link ResponseStatusExceptionResolver} for exceptions annotated with @{@link ResponseStatus}.
+ * {@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
+ *
+ *
+ * Registers the following other instances:
+ *
+ * {@link FormattingConversionService} for use with annotated controller methods and the spring:eval JSP tag.
+ * {@link Validator} for validating model attributes on annotated controller methods.
+ *
+ *
+ * @see EnableWebMvc
+ * @see WebMvcConfigurer
+ * @see WebMvcConfigurerAdapter
+ *
+ * @author Rossen Stoyanchev
+ * @since 3.1
+ */
+public abstract class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
+
+ private ServletContext servletContext;
+
+ private ApplicationContext applicationContext;
+
+ private List interceptors;
+
+ private List> messageConverters;
+
+ public void setServletContext(ServletContext servletContext) {
+ this.servletContext = servletContext;
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
+ /**
+ * Returns a {@link RequestMappingHandlerMapping} ordered at 0 for mapping requests to annotated controllers.
+ */
+ @Bean
+ public RequestMappingHandlerMapping requestMappingHandlerMapping() {
+ RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
+ mapping.setInterceptors(getInterceptors());
+ mapping.setOrder(0);
+ return mapping;
+ }
+
+ /**
+ * Provides access to the shared handler interceptors used to configure {@link HandlerMapping} instances with.
+ * This method cannot be overridden, use {@link #configureInterceptors(InterceptorConfigurer)} instead.
+ */
+ protected final Object[] getInterceptors() {
+ if (interceptors == null) {
+ InterceptorConfigurer configurer = new InterceptorConfigurer();
+ configureInterceptors(configurer);
+ configurer.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
+ interceptors = configurer.getInterceptors();
+ }
+ return interceptors.toArray();
+ }
+
+ /**
+ * Override this method to configure handler interceptors including interceptors mapped to path patterns.
+ * @see InterceptorConfigurer
+ */
+ protected void configureInterceptors(InterceptorConfigurer configurer) {
+ }
+
+ /**
+ * Returns a {@link SimpleUrlHandlerMapping} ordered at 1 to map URL paths directly to view names.
+ * To configure view controllers see {@link #configureViewControllers(ViewControllerConfigurer)}.
+ */
+ @Bean
+ public SimpleUrlHandlerMapping viewControllerHandlerMapping() {
+ ViewControllerConfigurer configurer = new ViewControllerConfigurer();
+ configurer.setOrder(1);
+ configureViewControllers(configurer);
+
+ SimpleUrlHandlerMapping handlerMapping = configurer.getHandlerMapping();
+ handlerMapping.setInterceptors(getInterceptors());
+ return handlerMapping;
+ }
+
+ /**
+ * Override this method to configure view controllers. View controllers provide a direct mapping between a
+ * URL path and view name. This is useful when serving requests that don't require application-specific
+ * controller logic and can be forwarded directly to a view for rendering.
+ * @see ViewControllerConfigurer
+ */
+ protected void configureViewControllers(ViewControllerConfigurer configurer) {
+ }
+
+ /**
+ * Returns a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL paths to controller bean names.
+ */
+ @Bean
+ public BeanNameUrlHandlerMapping beanNameHandlerMapping() {
+ BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
+ mapping.setOrder(2);
+ mapping.setInterceptors(getInterceptors());
+ return mapping;
+ }
+
+ /**
+ * Returns a {@link SimpleUrlHandlerMapping} ordered at Integer.MAX_VALUE-1 to serve static resource requests.
+ * To configure resource handling, see {@link #configureResourceHandling(ResourceConfigurer)}.
+ */
+ @Bean
+ public SimpleUrlHandlerMapping resourceHandlerMapping() {
+ ResourceConfigurer configurer = new ResourceConfigurer(applicationContext, servletContext);
+ configurer.setOrder(Integer.MAX_VALUE-1);
+ configureResourceHandling(configurer);
+ return configurer.getHandlerMapping();
+ }
+
+ /**
+ * Override this method to configure serving static resources such as images and css files through Spring MVC.
+ * @see ResourceConfigurer
+ */
+ protected void configureResourceHandling(ResourceConfigurer configurer) {
+ }
+
+ /**
+ * Returns a {@link SimpleUrlHandlerMapping} ordered at Integer.MAX_VALUE to serve static resources by
+ * forwarding to the Servlet container's default servlet. To configure default servlet handling see
+ * {@link #configureDefaultServletHandling(DefaultServletHandlerConfigurer)}.
+ */
+ @Bean
+ public SimpleUrlHandlerMapping defaultServletHandlerMapping() {
+ DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext);
+ configureDefaultServletHandling(configurer);
+ return configurer.getHandlerMapping();
+ }
+
+ /**
+ * Override this method to configure serving static resources through the Servlet container's default Servlet.
+ * @see DefaultServletHandlerConfigurer
+ */
+ protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
+ }
+
+ /**
+ * Returns a {@link RequestMappingHandlerAdapter} for processing requests using annotated controller methods.
+ * Also see {@link #initWebBindingInitializer()} for configuring data binding globally.
+ */
+ @Bean
+ public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
+ ConfigurableWebBindingInitializer webBindingInitializer = new ConfigurableWebBindingInitializer();
+ webBindingInitializer.setConversionService(mvcConversionService());
+ webBindingInitializer.setValidator(mvcValidator());
+ extendWebBindingInitializer(webBindingInitializer);
+
+ RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
+ adapter.setMessageConverters(getMessageConverters());
+ adapter.setWebBindingInitializer(webBindingInitializer);
+ return adapter;
+ }
+
+ /**
+ * Override this method to customize the {@link ConfigurableWebBindingInitializer} the
+ * {@link RequestMappingHandlerAdapter} is configured with.
+ */
+ protected void extendWebBindingInitializer(ConfigurableWebBindingInitializer webBindingInitializer) {
+ }
+
+ /**
+ * Provides access to the shared {@link HttpMessageConverter}s used by the
+ * {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}.
+ * This method cannot be extended directly, use {@link #configureMessageConverters(List)} add custom converters.
+ * Also see {@link #addDefaultHttpMessageConverters(List)} to easily add a set of default converters.
+ */
+ protected final List> getMessageConverters() {
+ if (messageConverters == null) {
+ messageConverters = new ArrayList>();
+ configureMessageConverters(messageConverters);
+ if (messageConverters.isEmpty()) {
+ addDefaultHttpMessageConverters(messageConverters);
+ }
+ }
+ return messageConverters;
+ }
+
+ /**
+ * Override this method to add custom {@link HttpMessageConverter}s to use with
+ * the {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}.
+ * If any converters are added, default converters will not be added automatically.
+ * See {@link #addDefaultHttpMessageConverters(List)} for adding default converters to the list.
+ * @param messageConverters the list to add converters to
+ */
+ protected void configureMessageConverters(List> converters) {
+ }
+
+ /**
+ * A method available to subclasses for adding default {@link HttpMessageConverter}s.
+ * @param messageConverters the list to add converters to
+ */
+ protected final void addDefaultHttpMessageConverters(List> messageConverters) {
+ StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
+ stringConverter.setWriteAcceptCharset(false);
+
+ messageConverters.add(new ByteArrayHttpMessageConverter());
+ messageConverters.add(stringConverter);
+ messageConverters.add(new ResourceHttpMessageConverter());
+ messageConverters.add(new SourceHttpMessageConverter());
+ messageConverters.add(new XmlAwareFormHttpMessageConverter());
+
+ ClassLoader classLoader = getClass().getClassLoader();
+ if (ClassUtils.isPresent("javax.xml.bind.Binder", classLoader)) {
+ messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
+ }
+ if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoader)) {
+ messageConverters.add(new MappingJacksonHttpMessageConverter());
+ }
+ if (ClassUtils.isPresent("com.sun.syndication.feed.WireFeed", classLoader)) {
+ messageConverters.add(new AtomFeedHttpMessageConverter());
+ messageConverters.add(new RssChannelHttpMessageConverter());
+ }
+ }
+
+ /**
+ * Returns a {@link FormattingConversionService} for use with annotated controller methods and the
+ * {@code spring:eval} JSP tag.
+ */
+ @Bean
+ public FormattingConversionService mvcConversionService() {
+ return new DefaultFormattingConversionService();
+ }
+
+ /**
+ * Returns {@link Validator} for validating {@code @ModelAttribute} and {@code @RequestBody} arguments of
+ * annotated controller methods. If a JSR-303 implementation is available on the classpath, the returned
+ * instance is LocalValidatorFactoryBean. Otherwise a no-op validator is returned.
+ */
+ @Bean
+ public Validator mvcValidator() {
+ if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
+ Class> clazz;
+ try {
+ String className = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean";
+ clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ throw new BeanInitializationException("Could not find default validator");
+ } catch (LinkageError e) {
+ throw new BeanInitializationException("Could not find default validator");
+ }
+ return (Validator) BeanUtils.instantiate(clazz);
+ }
+ else {
+ return new Validator() {
+ public boolean supports(Class> clazz) {
+ return false;
+ }
+ public void validate(Object target, Errors errors) {
+ }
+ };
+ }
+ }
+
+ /**
+ * Returns a {@link HttpRequestHandlerAdapter} for processing requests with {@link HttpRequestHandler}s.
+ */
+ @Bean
+ public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
+ return new HttpRequestHandlerAdapter();
+ }
+
+ /**
+ * Returns a {@link SimpleControllerHandlerAdapter} for processing requests with interface-based controllers.
+ */
+ @Bean
+ public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() {
+ return new SimpleControllerHandlerAdapter();
+ }
+
+ /**
+ * Returns a {@link HandlerExceptionResolverComposite} with this chain of exception resolvers:
+ *
+ * {@link ExceptionHandlerExceptionResolver} for handling exceptions through @{@link ExceptionHandler} methods.
+ * {@link ResponseStatusExceptionResolver} for exceptions annotated with @{@link ResponseStatus}.
+ * {@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
+ *
+ */
+ @Bean
+ public HandlerExceptionResolverComposite handlerExceptionResolver() throws Exception {
+ ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
+ exceptionHandlerExceptionResolver.setMessageConverters(getMessageConverters());
+ exceptionHandlerExceptionResolver.afterPropertiesSet();
+
+ List exceptionResolvers = new ArrayList();
+ exceptionResolvers.add(exceptionHandlerExceptionResolver);
+ exceptionResolvers.add(new ResponseStatusExceptionResolver());
+ exceptionResolvers.add(new DefaultHandlerExceptionResolver());
+
+ HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
+ composite.setOrder(0);
+ composite.setExceptionResolvers(exceptionResolvers);
+ return composite;
+ }
+
+}
diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
index 59e392fc5b6..5076ffb3510 100644
--- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
+++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java
@@ -18,7 +18,6 @@ package org.springframework.web.servlet.config.annotation;
import java.util.List;
-import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
@@ -35,14 +34,13 @@ import org.springframework.web.servlet.HandlerInterceptor;
import com.sun.corba.se.impl.presentation.rmi.ExceptionHandler;
/**
- * Defines options for customizing or adding to the default Spring MVC configuration enabled through the use
- * of @{@link EnableWebMvc}. The @{@link Configuration} class annotated with @{@link EnableWebMvc}
- * is the most obvious place to implement this interface. However all @{@link Configuration} classes and more generally
- * all Spring beans that implement this interface will be detected at startup and given a chance to customize Spring
- * MVC configuration provided it is enabled through @{@link EnableWebMvc}.
- *
- * Implementations of this interface will find it convenient to extend {@link WebMvcConfigurerAdapter} that
- * provides default method implementations and allows overriding only methods of interest.
+ * Defines configuration callback methods for customizing the default Spring MVC configuration enabled through the
+ * use of @{@link EnableWebMvc}.
+ *
+ *
Classes annotated with @{@link EnableWebMvc} can implement this interface in order to be called back and
+ * given a chance to customize the default configuration. The most convenient way to implement this interface is
+ * by extending from {@link WebMvcConfigurerAdapter}, which provides empty method implementations and allows
+ * overriding only the callback methods you're interested in.
*
* @author Rossen Stoyanchev
* @author Keith Donald
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java
index 180a88fb1c4..1f1534f6ea1 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/InterceptorConfigurerTests.java
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.config.annotation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
@@ -146,9 +147,18 @@ public class InterceptorConfigurerTests {
private List getInterceptorsForPath(String lookupPath) {
PathMatcher pathMatcher = new AntPathMatcher();
List result = new ArrayList();
- for (MappedInterceptor interceptor : configurer.getInterceptors()) {
- if (interceptor.matches(lookupPath, pathMatcher)) {
- result.add(interceptor.getInterceptor());
+ for (Object i : configurer.getInterceptors()) {
+ if (i instanceof MappedInterceptor) {
+ MappedInterceptor mappedInterceptor = (MappedInterceptor) i;
+ if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
+ result.add(mappedInterceptor.getInterceptor());
+ }
+ }
+ else if (i instanceof HandlerInterceptor){
+ result.add((HandlerInterceptor) i);
+ }
+ else {
+ fail("Unexpected interceptor type: " + i.getClass().getName());
}
}
return result;
diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java
index 8b51cc1b523..07e2a1ed00d 100644
--- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java
+++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java
@@ -18,6 +18,7 @@ package org.springframework.web.servlet.config.annotation;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
@@ -126,7 +127,7 @@ public class WebMvcConfigurationTests {
replay(configurer);
mvcConfiguration.setConfigurers(Arrays.asList(configurer));
- mvcConfiguration.validator();
+ mvcConfiguration.mvcValidator();
verify(configurer);
}
@@ -137,28 +138,25 @@ public class WebMvcConfigurationTests {
replay(configurer);
mvcConfiguration.setConfigurers(Arrays.asList(configurer));
- mvcConfiguration.validator();
+ mvcConfiguration.mvcValidator();
verify(configurer);
}
+ @SuppressWarnings("unchecked")
@Test
public void handlerExceptionResolver() throws Exception {
- Capture>> converters = new Capture>>();
- Capture> exceptionResolvers = new Capture>();
-
- configurer.configureMessageConverters(capture(converters));
- configurer.configureHandlerExceptionResolvers(capture(exceptionResolvers));
+ configurer.configureMessageConverters(isA(List.class));
+ configurer.configureHandlerExceptionResolvers(isA(List.class));
replay(configurer);
mvcConfiguration.setConfigurers(Arrays.asList(configurer));
- mvcConfiguration.handlerExceptionResolver();
+ List actual = mvcConfiguration.handlerExceptionResolver().getExceptionResolvers();
- assertEquals(3, exceptionResolvers.getValue().size());
- assertTrue(exceptionResolvers.getValue().get(0) instanceof ExceptionHandlerExceptionResolver);
- assertTrue(exceptionResolvers.getValue().get(1) instanceof ResponseStatusExceptionResolver);
- assertTrue(exceptionResolvers.getValue().get(2) instanceof DefaultHandlerExceptionResolver);
- assertTrue(converters.getValue().size() > 0);
+ assertEquals(3, actual.size());
+ assertTrue(actual.get(0) instanceof ExceptionHandlerExceptionResolver);
+ assertTrue(actual.get(1) instanceof ResponseStatusExceptionResolver);
+ assertTrue(actual.get(2) instanceof DefaultHandlerExceptionResolver);
verify(configurer);
}