From 726e920857d5c7ca69e0c8881db55c8ad6e878c3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 11 May 2011 18:02:07 +0000 Subject: [PATCH] Rename EnableMvcConfiguration->EnableWebMvc, refine method names in WebMvcConfigurer, fix issue with MappedInterceptors --- .../DefaultServletHandlerConfigurer.java | 2 +- ...vcConfiguration.java => EnableWebMvc.java} | 28 ++--- .../annotation/InterceptorConfigurer.java | 7 +- ...guration.java => WebMvcConfiguration.java} | 107 ++++++++++-------- ...cConfigurer.java => WebMvcConfigurer.java} | 49 ++++---- ...port.java => WebMvcConfigurerAdapter.java} | 29 ++--- ...te.java => WebMvcConfigurerComposite.java} | 50 ++++---- .../HandlerExceptionResolverComposite.java | 17 ++- .../InterceptorConfigurerTests.java | 4 +- ...sts.java => WebMvcConfigurationTests.java} | 96 ++++++++++++++-- 10 files changed, 250 insertions(+), 139 deletions(-) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/{EnableMvcConfiguration.java => EnableWebMvc.java} (79%) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/{MvcConfiguration.java => WebMvcConfiguration.java} (81%) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/{MvcConfigurer.java => WebMvcConfigurer.java} (63%) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/{MvcConfigurerSupport.java => WebMvcConfigurerAdapter.java} (75%) rename org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/{MvcConfigurerComposite.java => WebMvcConfigurerComposite.java} (59%) rename org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/{MvcConfigurationTests.java => WebMvcConfigurationTests.java} (56%) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/DefaultServletHandlerConfigurer.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/DefaultServletHandlerConfigurer.java index d4f8f78ace6..e9b713fc7ea 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/DefaultServletHandlerConfigurer.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/DefaultServletHandlerConfigurer.java @@ -34,7 +34,7 @@ import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler * by the Servlet container's default servlet. * *

It is important the configured handler remains last in the order of all {@link HandlerMapping} instances in - * the Spring MVC web application context. That is is the case if relying on @{@link EnableMvcConfiguration}. + * the Spring MVC web application context. That is is the case if relying on @{@link EnableWebMvc}. * However, if you register your own HandlerMapping instance sure to set its "order" property to a value lower * than that of the {@link DefaultServletHttpRequestHandler}, which is {@link Integer#MAX_VALUE}. * diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableMvcConfiguration.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java similarity index 79% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableMvcConfiguration.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java index 705705314f9..e16bd2d91e3 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableMvcConfiguration.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java @@ -30,28 +30,28 @@ import org.springframework.web.servlet.DispatcherServlet; * in @{@link Controller} classes. *

  * @Configuration
- * @EnableMvcConfiguration
+ * @EnableWebMvc
  * @ComponentScan(
- *	basePackageClasses = { MyMvcConfiguration.class },
+ *	basePackageClasses = { MyConfiguration.class },
  * 	excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Configuration.class) }
  * )
- * public class MyMvcConfiguration {
+ * public class MyConfiguration {
  *
  * }
  * 
- *

To customize the imported configuration you simply implement {@link MvcConfigurer}, or more likely extend - * {@link MvcConfigurerSupport} overriding selected methods only. The most obvious place to do this is - * the @{@link Configuration} class that enabled the Spring MVC configuration via @{@link EnableMvcConfiguration}. - * However any @{@link Configuration} class and more generally any Spring bean can implement {@link MvcConfigurer} + *

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. *

  * @Configuration
- * @EnableMvcConfiguration
+ * @EnableWebMvc
  * @ComponentScan(
- * 	basePackageClasses = { MyMvcConfiguration.class },
+ * 	basePackageClasses = { MyConfiguration.class },
  * 	excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Configuration.class) }
  * )
- * public class MyMvcConfiguration extends MvcConfigurerSupport {
+ * public class MyConfiguration extends WebMvcConfigurerAdapter {
  *
  * 	@Override
  * 	public void registerFormatters(FormatterRegistry formatterRegistry) {
@@ -68,8 +68,8 @@ import org.springframework.web.servlet.DispatcherServlet;
  * }
  * 
* - * @see MvcConfigurer - * @see MvcConfigurerSupport + * @see WebMvcConfigurer + * @see WebMvcConfigurerAdapter * * @author Dave Syer * @author Rossen Stoyanchev @@ -78,6 +78,6 @@ import org.springframework.web.servlet.DispatcherServlet; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented -@Import(MvcConfiguration.class) -public @interface EnableMvcConfiguration { +@Import(WebMvcConfiguration.class) +public @interface 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 e52c7b4e2a7..0cb67fb3bae 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 @@ -24,7 +24,6 @@ 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.MappedInterceptors; import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; /** @@ -117,10 +116,10 @@ public class InterceptorConfigurer { } /** - * Returns a {@link MappedInterceptors} instance with all registered interceptors. + * Returns all registered interceptors. */ - protected MappedInterceptors getMappedInterceptors() { - return new MappedInterceptors(mappedInterceptors.toArray(new MappedInterceptor[mappedInterceptors.size()])); + protected MappedInterceptor[] getInterceptors() { + return mappedInterceptors.toArray(new MappedInterceptor[mappedInterceptors.size()]); } } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfiguration.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java similarity index 81% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfiguration.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java index 655cf7185ae..fe38c31a3dd 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfiguration.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfiguration.java @@ -58,6 +58,7 @@ 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.MappedInterceptors; import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; import org.springframework.web.servlet.mvc.Controller; @@ -72,10 +73,10 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv /** * 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 EnableMvcConfiguration} annotation. + * enabled through the {@link EnableWebMvc} annotation. * *

A number of options are available for customizing the default configuration provided by this class. - * See {@link EnableMvcConfiguration} and {@link MvcConfigurer} for details. + * See {@link EnableWebMvc} and {@link WebMvcConfigurer} for details. * *

Registers these handler mappings: *

* *

Note: that the SimpleUrlHandlerMapping instances above will have empty URL maps and - * hence no effect until explicitly configured via {@link MvcConfigurer}. + * hence no effect until explicitly configured via {@link WebMvcConfigurer}. * *

Registers these handler adapters: *

* - * @see EnableMvcConfiguration - * @see MvcConfigurer + * @see EnableWebMvc + * @see WebMvcConfigurer * * @author Rossen Stoyanchev * @since 3.1 */ @Configuration -class MvcConfiguration implements ApplicationContextAware, ServletContextAware { +class WebMvcConfiguration implements ApplicationContextAware, ServletContextAware { - private final MvcConfigurerComposite configurers = new MvcConfigurerComposite(); + private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); private ServletContext servletContext; private ApplicationContext applicationContext; @Autowired(required = false) - public void setConfigurers(List configurers) { + public void setConfigurers(List configurers) { this.configurers.addConfigurers(configurers); } @@ -139,29 +140,41 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { } @Bean - RequestMappingHandlerMapping requestMappingHandlerMapping() { + public RequestMappingHandlerMapping requestMappingHandlerMapping() { RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); + mapping.setMappedInterceptors(getMappedInterceptors()); mapping.setOrder(0); return mapping; } - @Bean - HandlerMapping viewControllerHandlerMapping() { - ViewControllerConfigurer configurer = new ViewControllerConfigurer(); - configurer.setOrder(1); - configurers.addViewControllers(configurer); - return configurer.getHandlerMapping(); + private MappedInterceptor[] getMappedInterceptors() { + InterceptorConfigurer configurer = new InterceptorConfigurer(); + configurers.configureInterceptors(configurer); + configurer.addInterceptor(new ConversionServiceExposingInterceptor(conversionService())); + return configurer.getInterceptors(); } @Bean - BeanNameUrlHandlerMapping beanNameHandlerMapping() { + public HandlerMapping viewControllerHandlerMapping() { + ViewControllerConfigurer configurer = new ViewControllerConfigurer(); + configurer.setOrder(1); + configurers.configureViewControllers(configurer); + + SimpleUrlHandlerMapping handlerMapping = configurer.getHandlerMapping(); + handlerMapping.setMappedInterceptors(getMappedInterceptors()); + return handlerMapping; + } + + @Bean + public BeanNameUrlHandlerMapping beanNameHandlerMapping() { BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); mapping.setOrder(2); + mapping.setMappedInterceptors(getMappedInterceptors()); return mapping; } @Bean - HandlerMapping resourceHandlerMapping() { + public HandlerMapping resourceHandlerMapping() { ResourceConfigurer configurer = new ResourceConfigurer(applicationContext, servletContext); configurer.setOrder(Integer.MAX_VALUE-1); configurers.configureResourceHandling(configurer); @@ -169,14 +182,14 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { } @Bean - HandlerMapping defaultServletHandlerMapping() { + public HandlerMapping defaultServletHandlerMapping() { DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); configurers.configureDefaultServletHandling(configurer); return configurer.getHandlerMapping(); } @Bean - RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); ConfigurableWebBindingInitializer bindingInitializer = new ConfigurableWebBindingInitializer(); @@ -185,29 +198,32 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { adapter.setWebBindingInitializer(bindingInitializer); List argumentResolvers = new ArrayList(); - configurers.addCustomArgumentResolvers(argumentResolvers); + configurers.addArgumentResolvers(argumentResolvers); adapter.setCustomArgumentResolvers(argumentResolvers); List returnValueHandlers = new ArrayList(); - configurers.addCustomReturnValueHandlers(returnValueHandlers); + configurers.addReturnValueHandlers(returnValueHandlers); adapter.setCustomReturnValueHandlers(returnValueHandlers); - List> converters = getDefaultHttpMessageConverters(); + List> converters = new ArrayList>(); configurers.configureMessageConverters(converters); + if (converters.size() == 0) { + addDefaultHttpMessageConverters(converters); + } adapter.setMessageConverters(converters); return adapter; } @Bean(name="mvcConversionService") - FormattingConversionService conversionService() { + public FormattingConversionService conversionService() { FormattingConversionService conversionService = new DefaultFormattingConversionService(); - configurers.registerFormatters(conversionService); + configurers.addFormatters(conversionService); return conversionService; } @Bean(name="mvcValidator") - Validator validator() { + public Validator validator() { Validator validator = configurers.getValidator(); if (validator != null) { return validator; @@ -216,7 +232,7 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { Class clazz; try { String className = "org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"; - clazz = ClassUtils.forName(className, MvcConfiguration.class.getClassLoader()); + clazz = ClassUtils.forName(className, WebMvcConfiguration.class.getClassLoader()); } catch (ClassNotFoundException e) { throw new BeanInitializationException("Could not find default validator"); } catch (LinkageError e) { @@ -236,11 +252,10 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { } } - private List> getDefaultHttpMessageConverters() { + private void addDefaultHttpMessageConverters(List> converters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setWriteAcceptCharset(false); - List> converters = new ArrayList>(); converters.add(new ByteArrayHttpMessageConverter()); converters.add(stringConverter); converters.add(new ResourceHttpMessageConverter()); @@ -258,28 +273,29 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { converters.add(new AtomFeedHttpMessageConverter()); converters.add(new RssChannelHttpMessageConverter()); } - - return converters; } @Bean - HttpRequestHandlerAdapter httpRequestHandlerAdapter() { + public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { return new HttpRequestHandlerAdapter(); } @Bean - SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { + public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { return new SimpleControllerHandlerAdapter(); } @Bean - HandlerExceptionResolver handlerExceptionResolver() throws Exception { + public HandlerExceptionResolver handlerExceptionResolver() throws Exception { List resolvers = new ArrayList(); - resolvers.add(createExceptionHandlerExceptionResolver()); - resolvers.add(new ResponseStatusExceptionResolver()); - resolvers.add(new DefaultHandlerExceptionResolver()); configurers.configureHandlerExceptionResolvers(resolvers); + if (resolvers.size() == 0) { + resolvers.add(createExceptionHandlerExceptionResolver()); + resolvers.add(new ResponseStatusExceptionResolver()); + resolvers.add(new DefaultHandlerExceptionResolver()); + } + HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); composite.setExceptionResolvers(resolvers); @@ -288,22 +304,17 @@ class MvcConfiguration implements ApplicationContextAware, ServletContextAware { private HandlerExceptionResolver createExceptionHandlerExceptionResolver() throws Exception { ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver(); - - List> converters = getDefaultHttpMessageConverters(); - configurers.configureMessageConverters(converters); - resolver.setMessageConverters(converters); resolver.setOrder(0); + List> converters = new ArrayList>(); + configurers.configureMessageConverters(converters); + if (converters.size() == 0) { + addDefaultHttpMessageConverters(converters); + } + resolver.setMessageConverters(converters); + resolver.afterPropertiesSet(); return resolver; } - @Bean - MappedInterceptors mappedInterceptors() { - InterceptorConfigurer configurer = new InterceptorConfigurer(); - configurer.addInterceptor(new ConversionServiceExposingInterceptor(conversionService())); - configurers.addInterceptors(configurer); - return configurer.getMappedInterceptors(); - } - } diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurer.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java similarity index 63% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurer.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index 8537d713066..3ae69ce03fa 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurer.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -36,12 +36,12 @@ 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 EnableMvcConfiguration}. The @{@link Configuration} class annotated with @{@link EnableMvcConfiguration} + * 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 EnableMvcConfiguration}. + * MVC configuration provided it is enabled through @{@link EnableWebMvc}. * - *

Implementations of this interface will find it convenient to extend {@link MvcConfigurerSupport} that + *

Implementations of this interface will find it convenient to extend {@link WebMvcConfigurerAdapter} that * provides default method implementations and allows overriding only methods of interest. * * @author Rossen Stoyanchev @@ -49,17 +49,18 @@ import com.sun.corba.se.impl.presentation.rmi.ExceptionHandler; * @author David Syer * @since 3.1 */ -public interface MvcConfigurer { +public interface WebMvcConfigurer { /** - * Register application-specific {@link Converter}s and {@link Formatter}s for use in Spring MVC. + * Add {@link Converter}s and {@link Formatter}s in addition to the ones registered by default. */ - void registerFormatters(FormatterRegistry formatterRegistry); + void addFormatters(FormatterRegistry formatterRegistry); /** - * Customize the list of {@link HttpMessageConverter}s to use when resolving method arguments or handling - * return values from @{@link RequestMapping} and @{@link ExceptionHandler} methods. - * @param converters the list of converters, initially populated with the default set of converters + * Configure the list of {@link HttpMessageConverter}s to use when resolving method arguments or handling + * return values in @{@link RequestMapping} and @{@link ExceptionHandler} methods. + * Specifying custom converters overrides the converters registered by default. + * @param converters a list to add message converters to */ void configureMessageConverters(List> converters); @@ -71,37 +72,43 @@ public interface MvcConfigurer { Validator getValidator(); /** - * Add custom {@link HandlerMethodArgumentResolver}s to use for resolving argument values - * on @{@link RequestMapping} and @{@link ExceptionHandler} methods. + * Add custom {@link HandlerMethodArgumentResolver}s to use in addition to the ones registered by default. + *

Generally custom argument resolvers are invoked first. However this excludes default argument resolvers that + * rely on the presence of annotations (e.g. {@code @RequestParameter}, {@code @PathVariable}, etc.). Those + * argument resolvers are not customizable without configuring RequestMappingHandlerAdapter directly. * @param argumentResolvers the list of custom converters, initially empty */ - void addCustomArgumentResolvers(List argumentResolvers); + void addArgumentResolvers(List argumentResolvers); /** - * Add custom {@link HandlerMethodReturnValueHandler}s to use for handling return values - * from @{@link RequestMapping} and @{@link ExceptionHandler} methods. + * Add custom {@link HandlerMethodReturnValueHandler}s to in addition to the ones registered by default. + *

Generally custom return value handlers are invoked first. However this excludes default return value handlers + * that rely on the presence of annotations (e.g. {@code @ResponseBody}, {@code @ModelAttribute}, etc.). Those + * handlers are not customizable without configuring RequestMappingHandlerAdapter directly. * @param returnValueHandlers the list of custom handlers, initially empty */ - void addCustomReturnValueHandlers(List returnValueHandlers); + void addReturnValueHandlers(List returnValueHandlers); /** - * Customize the list of {@link HandlerExceptionResolver}s to use for handling controller exceptions. - * @param exceptionResolvers the list of resolvers, initially populated with the default set of resolvers + * Configure the list of {@link HandlerExceptionResolver}s to use for handling unresolved controller exceptions. + * Specifying exception resolvers overrides the ones registered by default. + * @param exceptionResolvers a list to add exception resolvers to */ void configureHandlerExceptionResolvers(List exceptionResolvers); /** - * Add Spring MVC interceptors. Interceptors can be of type {@link HandlerInterceptor} or + * Configure the Spring MVC interceptors to use. Interceptors can be of type {@link HandlerInterceptor} or * {@link WebRequestInterceptor}. They allow requests to be pre/post processed before/after controller * invocation. Interceptors can be registered to apply to all requests or limited to a set of path patterns. * @see InterceptorConfigurer */ - void addInterceptors(InterceptorConfigurer interceptorConfigurer); + void configureInterceptors(InterceptorConfigurer interceptorConfigurer); /** - * Map URL paths to view names. This is convenient when a request can be rendered without a controller. + * Configure the view controllers to use. A view controller is used to map a URL path directly to a view name. + * This is convenient when a request does not require controller logic. */ - void addViewControllers(ViewControllerConfigurer viewControllerConfigurer); + void configureViewControllers(ViewControllerConfigurer viewControllerConfigurer); /** * Configure a handler for serving static resources such as images, js, and, css files through Spring MVC diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerSupport.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java similarity index 75% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerSupport.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java index 37569518c36..671fc54f3cd 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerSupport.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java @@ -22,22 +22,23 @@ import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; /** - * An abstract class with empty method implementations of the {@link MvcConfigurer} interface for a simplified - * implementation of {@link MvcConfigurer} so that subclasses can override selected methods only. + * An abstract class with empty method implementations of {@link WebMvcConfigurer}. + * Subclasses can override only the methods they need. * * @author Rossen Stoyanchev * @since 3.1 */ -public abstract class MvcConfigurerSupport implements MvcConfigurer { +public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer { /** * {@inheritDoc} *

This implementation is empty. */ - public void registerFormatters(FormatterRegistry formatterRegistry) { + public void addFormatters(FormatterRegistry formatterRegistry) { } /** @@ -47,13 +48,6 @@ public abstract class MvcConfigurerSupport implements MvcConfigurer { public void configureMessageConverters(List> converters) { } - /** - * {@inheritDoc} - *

This implementation is empty. - */ - public void configureValidator(Validator validator) { - } - /** * {@inheritDoc} *

This implementation returns {@code null} @@ -66,7 +60,14 @@ public abstract class MvcConfigurerSupport implements MvcConfigurer { * {@inheritDoc} *

This implementation is empty. */ - public void addCustomArgumentResolvers(List argumentResolvers) { + public void addArgumentResolvers(List argumentResolvers) { + } + + /** + * {@inheritDoc} + *

This implementation is empty. + */ + public void addReturnValueHandlers(List returnValueHandlers) { } /** @@ -80,14 +81,14 @@ public abstract class MvcConfigurerSupport implements MvcConfigurer { * {@inheritDoc} *

This implementation is empty. */ - public void addInterceptors(InterceptorConfigurer interceptorConfigurer) { + public void configureInterceptors(InterceptorConfigurer interceptorConfigurer) { } /** * {@inheritDoc} *

This implementation is empty. */ - public void addViewControllers(ViewControllerConfigurer viewControllerConfigurer) { + public void configureViewControllers(ViewControllerConfigurer viewControllerConfigurer) { } /** diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerComposite.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java similarity index 59% rename from org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerComposite.java rename to org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java index 1d90bdf9ee2..efc09b3ae1e 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/MvcConfigurerComposite.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java @@ -29,78 +29,78 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; /** - * An {@link MvcConfigurer} implementation that delegates to other {@link MvcConfigurer} instances. + * An {@link WebMvcConfigurer} implementation that delegates to other {@link WebMvcConfigurer} instances. * * @author Rossen Stoyanchev * @since 3.1 */ -class MvcConfigurerComposite implements MvcConfigurer { +class WebMvcConfigurerComposite implements WebMvcConfigurer { - private final List configurers = new ArrayList(); + private final List configurers = new ArrayList(); - void addConfigurers(List configurers) { + public void addConfigurers(List configurers) { if (configurers != null) { this.configurers.addAll(configurers); } } - public void registerFormatters(FormatterRegistry formatterRegistry) { - for (MvcConfigurer configurer : configurers) { - configurer.registerFormatters(formatterRegistry); + public void addFormatters(FormatterRegistry formatterRegistry) { + for (WebMvcConfigurer configurer : configurers) { + configurer.addFormatters(formatterRegistry); } } public void configureMessageConverters(List> converters) { - for (MvcConfigurer configurer : configurers) { + for (WebMvcConfigurer configurer : configurers) { configurer.configureMessageConverters(converters); } } - public void addCustomArgumentResolvers(List argumentResolvers) { - for (MvcConfigurer configurer : configurers) { - configurer.addCustomArgumentResolvers(argumentResolvers); + public void addArgumentResolvers(List argumentResolvers) { + for (WebMvcConfigurer configurer : configurers) { + configurer.addArgumentResolvers(argumentResolvers); } } - public void addCustomReturnValueHandlers(List returnValueHandlers) { - for (MvcConfigurer configurer : configurers) { - configurer.addCustomReturnValueHandlers(returnValueHandlers); + public void addReturnValueHandlers(List returnValueHandlers) { + for (WebMvcConfigurer configurer : configurers) { + configurer.addReturnValueHandlers(returnValueHandlers); } } public void configureHandlerExceptionResolvers(List exceptionResolvers) { - for (MvcConfigurer configurer : configurers) { + for (WebMvcConfigurer configurer : configurers) { configurer.configureHandlerExceptionResolvers(exceptionResolvers); } } - public void addInterceptors(InterceptorConfigurer interceptorRegistry) { - for (MvcConfigurer configurer : configurers) { - configurer.addInterceptors(interceptorRegistry); + public void configureInterceptors(InterceptorConfigurer interceptorRegistry) { + for (WebMvcConfigurer configurer : configurers) { + configurer.configureInterceptors(interceptorRegistry); } } - public void addViewControllers(ViewControllerConfigurer viewControllerConfigurer) { - for (MvcConfigurer configurer : configurers) { - configurer.addViewControllers(viewControllerConfigurer); + public void configureViewControllers(ViewControllerConfigurer viewControllerConfigurer) { + for (WebMvcConfigurer configurer : configurers) { + configurer.configureViewControllers(viewControllerConfigurer); } } public void configureResourceHandling(ResourceConfigurer resourceConfigurer) { - for (MvcConfigurer configurer : configurers) { + for (WebMvcConfigurer configurer : configurers) { configurer.configureResourceHandling(resourceConfigurer); } } public void configureDefaultServletHandling(DefaultServletHandlerConfigurer handlerConfigurer) { - for (MvcConfigurer configurer : configurers) { + for (WebMvcConfigurer configurer : configurers) { configurer.configureDefaultServletHandling(handlerConfigurer); } } public Validator getValidator() { - Map validators = new HashMap(); - for (MvcConfigurer configurer : configurers) { + Map validators = new HashMap(); + for (WebMvcConfigurer configurer : configurers) { Validator validator = configurer.getValidator(); if (validator != null) { validators.put(configurer, validator); diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/HandlerExceptionResolverComposite.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/HandlerExceptionResolverComposite.java index 9d3504f1831..d75b2e5bced 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/HandlerExceptionResolverComposite.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/handler/HandlerExceptionResolverComposite.java @@ -1,5 +1,6 @@ package org.springframework.web.servlet.handler; +import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -10,7 +11,7 @@ import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; /** - * A {@link HandlerExceptionResolver} that delegates to a list of {@link HandlerExceptionResolver}s. + * A {@link HandlerExceptionResolver} that delegates to a list of other {@link HandlerExceptionResolver}s. * * @author Rossen Stoyanchev * @since 3.1 @@ -29,10 +30,24 @@ public class HandlerExceptionResolverComposite implements HandlerExceptionResolv return this.order; } + /** + * Set the list of exception resolvers to delegate to. + */ public void setExceptionResolvers(List exceptionResolvers) { this.resolvers = exceptionResolvers; } + /** + * Return the list of exception resolvers to delegate to. + */ + public List getExceptionResolvers() { + return Collections.unmodifiableList(resolvers); + } + + /** + * Resolve the exception by iterating over the list of configured exception resolvers. + * The first one to return a ModelAndView instance wins. Otherwise {@code null} is returned. + */ public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, 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 1cf2590f00b..74434f38079 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 @@ -29,6 +29,7 @@ import org.springframework.util.AntPathMatcher; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.WebRequestInterceptor; import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.handler.MappedInterceptors; import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.theme.ThemeChangeInterceptor; @@ -139,7 +140,8 @@ public class InterceptorConfigurerTests { } private HandlerInterceptor[] getInterceptorsForPath(String lookupPath) { - return configurer.getMappedInterceptors().getInterceptors(lookupPath, new AntPathMatcher()); + MappedInterceptors mappedInterceptors = new MappedInterceptors(configurer.getInterceptors()); + return mappedInterceptors.getInterceptors(lookupPath, new AntPathMatcher()); } private void verifyAdaptedInterceptor(HandlerInterceptor interceptor, TestWebRequestInterceptor webInterceptor) diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/MvcConfigurationTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java similarity index 56% rename from org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/MvcConfigurationTests.java rename to org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java index 3749b9af7c5..baa0b40e766 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/MvcConfigurationTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationTests.java @@ -21,44 +21,55 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.servlet.http.HttpServletRequest; + import org.easymock.Capture; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; import org.springframework.format.support.FormattingConversionService; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.stereotype.Controller; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.context.support.StaticWebApplicationContext; 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.handler.HandlerExceptionResolverComposite; 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 test fixture with an {@link MvcConfiguration} and a mock {@link MvcConfigurer} for verifying delegation. + * A test fixture for WebMvcConfiguration tests. * * @author Rossen Stoyanchev */ -public class MvcConfigurationTests { +public class WebMvcConfigurationTests { - private MvcConfiguration mvcConfiguration; + private WebMvcConfiguration mvcConfiguration; - private MvcConfigurer configurer; + private WebMvcConfigurer configurer; @Before public void setUp() { - configurer = EasyMock.createMock(MvcConfigurer.class); - mvcConfiguration = new MvcConfiguration(); - mvcConfiguration.setConfigurers(Arrays.asList(configurer)); + configurer = EasyMock.createMock(WebMvcConfigurer.class); + mvcConfiguration = new WebMvcConfiguration(); } @Test @@ -69,12 +80,13 @@ public class MvcConfigurationTests { Capture>> converters = new Capture>>(); expect(configurer.getValidator()).andReturn(null); - configurer.registerFormatters(capture(conversionService)); - configurer.addCustomArgumentResolvers(capture(resolvers)); - configurer.addCustomReturnValueHandlers(capture(handlers)); + configurer.addFormatters(capture(conversionService)); + configurer.addArgumentResolvers(capture(resolvers)); + configurer.addReturnValueHandlers(capture(handlers)); configurer.configureMessageConverters(capture(converters)); replay(configurer); + mvcConfiguration.setConfigurers(Arrays.asList(configurer)); RequestMappingHandlerAdapter adapter = mvcConfiguration.requestMappingHandlerAdapter(); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer(); @@ -89,11 +101,30 @@ public class MvcConfigurationTests { verify(configurer); } + @Test + public void configureMessageConverters() { + RequestMappingHandlerAdapter adapter = mvcConfiguration.requestMappingHandlerAdapter(); + assertTrue("There should be at least two default converters ", adapter.getMessageConverters().size() > 1); + + List configurers = new ArrayList(); + configurers.add(new WebMvcConfigurerAdapter() { + @Override + public void configureMessageConverters(List> converters) { + converters.add(new StringHttpMessageConverter()); + } + }); + mvcConfiguration.setConfigurers(configurers ); + + adapter = mvcConfiguration.requestMappingHandlerAdapter(); + assertEquals("Only one custom converter should be registered", 1, adapter.getMessageConverters().size()); + } + @Test public void getCustomValidator() { expect(configurer.getValidator()).andReturn(new LocalValidatorFactoryBean()); replay(configurer); + mvcConfiguration.setConfigurers(Arrays.asList(configurer)); mvcConfiguration.validator(); verify(configurer); @@ -104,6 +135,7 @@ public class MvcConfigurationTests { expect(configurer.getValidator()).andReturn(null); replay(configurer); + mvcConfiguration.setConfigurers(Arrays.asList(configurer)); mvcConfiguration.validator(); verify(configurer); @@ -118,6 +150,7 @@ public class MvcConfigurationTests { configurer.configureHandlerExceptionResolvers(capture(exceptionResolvers)); replay(configurer); + mvcConfiguration.setConfigurers(Arrays.asList(configurer)); mvcConfiguration.handlerExceptionResolver(); assertEquals(3, exceptionResolvers.getValue().size()); @@ -129,4 +162,47 @@ public class MvcConfigurationTests { verify(configurer); } + @Test + public void configureExceptionResolvers() throws Exception { + HandlerExceptionResolverComposite composite; + + composite = (HandlerExceptionResolverComposite) mvcConfiguration.handlerExceptionResolver(); + assertTrue("Expected more than one exception resolver by default", composite.getExceptionResolvers().size() > 1); + + List configurers = new ArrayList(); + configurers.add(new WebMvcConfigurerAdapter() { + @Override + public void configureHandlerExceptionResolvers(List exceptionResolvers) { + exceptionResolvers.add(new DefaultHandlerExceptionResolver()); + } + }); + mvcConfiguration.setConfigurers(configurers); + + composite = (HandlerExceptionResolverComposite) mvcConfiguration.handlerExceptionResolver(); + assertEquals("Only one custom converter is expected", 1, composite.getExceptionResolvers().size()); + } + + @Test + public void configureInterceptors() throws Exception { + HttpServletRequest request = new MockHttpServletRequest("GET", "/"); + + StaticWebApplicationContext context = new StaticWebApplicationContext(); + context.registerSingleton("controller", TestHandler.class); + + RequestMappingHandlerMapping hm = mvcConfiguration.requestMappingHandlerMapping(); + hm.setApplicationContext(context); + HandlerExecutionChain chain = hm.getHandler(request); + assertNotNull("Expected at one default converter", chain.getInterceptors()); + } + + @Controller + private static class TestHandler { + + @SuppressWarnings("unused") + @RequestMapping("/") + public void handle() { + } + + } + }