From 2af294ab26b670c7f997054ab11ecf7402fee6ed Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 15 May 2012 17:02:06 -0400 Subject: [PATCH] Add MessageCodesResolver hook to WebMvcConfigurer This change makes it possible to provide a custom MessageCodesResolver through the MVC Java config whether using @EnableWebMvc and extending WebMVcConfigurerAdapter or sub-classing directly from WebMvcConfigurationSupport. Issue: SPR-9223 --- .../DelegatingWebMvcConfiguration.java | 41 +++--- .../WebMvcConfigurationSupport.java | 123 ++++++++++-------- .../config/annotation/WebMvcConfigurer.java | 46 ++++--- .../annotation/WebMvcConfigurerAdapter.java | 14 +- .../annotation/WebMvcConfigurerComposite.java | 58 +++++---- .../DelegatingWebMvcConfigurationTests.java | 29 +++-- .../WebMvcConfigurationSupportTests.java | 24 +++- src/dist/changelog.txt | 1 + 8 files changed, 208 insertions(+), 128 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index 68d2043269b4..82cde7ca1685 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,20 +22,20 @@ import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.validation.MessageCodesResolver; 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; /** - * Extends {@link WebMvcConfigurationSupport} with the ability to detect beans - * of type {@link WebMvcConfigurer} and give them a chance to customize the - * provided configuration by delegating to them at the appropriate times. - * + * A sub-class of {@code WebMvcConfigurationSupport} that detects and delegates + * to all beans of type {@link WebMvcConfigurer} allowing them to customize the + * configuration provided by {@code WebMvcConfigurationSupport}. This is the + * class actually imported by {@link EnableWebMvc @EnableWebMvc}. + * * @author Rossen Stoyanchev * @since 3.1 - * - * @see EnableWebMvc */ @Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { @@ -52,52 +52,57 @@ public void setConfigurers(List configurers) { @Override protected void addInterceptors(InterceptorRegistry registry) { - configurers.addInterceptors(registry); + this.configurers.addInterceptors(registry); } @Override protected void addViewControllers(ViewControllerRegistry registry) { - configurers.addViewControllers(registry); + this.configurers.addViewControllers(registry); } @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { - configurers.addResourceHandlers(registry); + this.configurers.addResourceHandlers(registry); } @Override protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - configurers.configureDefaultServletHandling(configurer); + this.configurers.configureDefaultServletHandling(configurer); } - + @Override protected void addArgumentResolvers(List argumentResolvers) { - configurers.addArgumentResolvers(argumentResolvers); + this.configurers.addArgumentResolvers(argumentResolvers); } @Override protected void addReturnValueHandlers(List returnValueHandlers) { - configurers.addReturnValueHandlers(returnValueHandlers); + this.configurers.addReturnValueHandlers(returnValueHandlers); } @Override protected void configureMessageConverters(List> converters) { - configurers.configureMessageConverters(converters); + this.configurers.configureMessageConverters(converters); } @Override protected void addFormatters(FormatterRegistry registry) { - configurers.addFormatters(registry); + this.configurers.addFormatters(registry); } @Override protected Validator getValidator() { - return configurers.getValidator(); + return this.configurers.getValidator(); + } + + @Override + protected MessageCodesResolver getMessageCodesResolver() { + return this.configurers.getMessageCodesResolver(); } @Override protected void configureHandlerExceptionResolvers(List exceptionResolvers) { - configurers.configureHandlerExceptionResolvers(exceptionResolvers); + this.configurers.configureHandlerExceptionResolvers(exceptionResolvers); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 55d418d80719..be25fcdd6d8b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -48,6 +48,7 @@ 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.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.HttpRequestHandler; @@ -280,6 +281,7 @@ public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { ConfigurableWebBindingInitializer webBindingInitializer = new ConfigurableWebBindingInitializer(); webBindingInitializer.setConversionService(mvcConversionService()); webBindingInitializer.setValidator(mvcValidator()); + webBindingInitializer.setMessageCodesResolver(getMessageCodesResolver()); List argumentResolvers = new ArrayList(); addArgumentResolvers(argumentResolvers); @@ -295,6 +297,69 @@ public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { return adapter; } + /** + * Returns a {@link FormattingConversionService} for use with annotated + * controller methods and the {@code spring:eval} JSP tag. + * Also see {@link #addFormatters} as an alternative to overriding this method. + */ + @Bean + public FormattingConversionService mvcConversionService() { + FormattingConversionService conversionService = new DefaultFormattingConversionService(); + addFormatters(conversionService); + return conversionService; + } + + /** + * Returns a global {@link Validator} instance for example for validating + * {@code @ModelAttribute} and {@code @RequestBody} method arguments. + * Delegates to {@link #getValidator()} first and if that returns {@code null} + * checks the classpath for the presence of a JSR-303 implementations + * before creating a {@code LocalValidatorFactoryBean}.If a JSR-303 + * implementation is not available, a no-op {@link Validator} is returned. + */ + @Bean + public Validator mvcValidator() { + Validator validator = getValidator(); + if (validator == null) { + 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", e); + } catch (LinkageError e) { + throw new BeanInitializationException("Could not find default validator", e); + } + validator = (Validator) BeanUtils.instantiate(clazz); + } + else { + validator = new Validator() { + public boolean supports(Class clazz) { + return false; + } + public void validate(Object target, Errors errors) { + } + }; + } + } + return validator; + } + + /** + * Override this method to provide a custom {@link Validator}. + */ + protected Validator getValidator() { + return null; + } + + /** + * Override this method to provide a custom {@link MessageCodesResolver}. + */ + protected MessageCodesResolver getMessageCodesResolver() { + return null; + } + /** * Add custom {@link HandlerMethodArgumentResolver}s to use in addition to * the ones registered by default. @@ -387,68 +452,12 @@ else if (ClassUtils.isPresent("org.codehaus.jackson.map.ObjectMapper", classLoad } } - /** - * Returns a {@link FormattingConversionService} for use with annotated - * controller methods and the {@code spring:eval} JSP tag. - * Also see {@link #addFormatters} as an alternative to overriding this method. - */ - @Bean - public FormattingConversionService mvcConversionService() { - FormattingConversionService conversionService = new DefaultFormattingConversionService(); - addFormatters(conversionService); - return conversionService; - } - /** * Override this method to add custom {@link Converter}s and {@link Formatter}s. */ protected void addFormatters(FormatterRegistry registry) { } - /** - * Returns a global {@link Validator} instance for example for validating - * {@code @ModelAttribute} and {@code @RequestBody} method arguments. - * Delegates to {@link #getValidator()} first and if that returns {@code null} - * checks the classpath for the presence of a JSR-303 implementations - * before creating a {@code LocalValidatorFactoryBean}.If a JSR-303 - * implementation is not available, a no-op {@link Validator} is returned. - */ - @Bean - public Validator mvcValidator() { - Validator validator = getValidator(); - if (validator == null) { - 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", e); - } catch (LinkageError e) { - throw new BeanInitializationException("Could not find default validator", e); - } - validator = (Validator) BeanUtils.instantiate(clazz); - } - else { - validator = new Validator() { - public boolean supports(Class clazz) { - return false; - } - public void validate(Object target, Errors errors) { - } - }; - } - } - return validator; - } - - /** - * Override this method to provide a custom {@link Validator}. - */ - protected Validator getValidator() { - return null; - } - /** * Returns a {@link HttpRequestHandlerAdapter} for processing requests * with {@link HttpRequestHandler}s. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index cc86beb8b7e4..7d077fadc84e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.springframework.format.Formatter; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; @@ -30,10 +31,10 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; /** - * Defines callback methods to customize the Java-based configuration for - * Spring MVC enabled via {@code @EnableWebMvc}. - * - *

{@code @EnableWebMvc}-annotated configuration classes may implement + * Defines callback methods to customize the Java-based configuration for + * Spring MVC enabled via {@code @EnableWebMvc}. + * + *

{@code @EnableWebMvc}-annotated configuration classes may implement * this interface to be called back and given a chance to customize the * default configuration. Consider extending {@link WebMvcConfigurerAdapter}, * which provides a stub implementation of all interface methods. @@ -46,7 +47,7 @@ public interface WebMvcConfigurer { /** - * Add {@link Converter}s and {@link Formatter}s in addition to the ones + * Add {@link Converter}s and {@link Formatter}s in addition to the ones * registered by default. */ void addFormatters(FormatterRegistry registry); @@ -69,25 +70,25 @@ public interface WebMvcConfigurer { Validator getValidator(); /** - * Add resolvers to support custom controller method argument types. - *

This does not override the built-in support for resolving handler - * method arguments. To customize the built-in support for argument + * Add resolvers to support custom controller method argument types. + *

This does not override the built-in support for resolving handler + * method arguments. To customize the built-in support for argument * resolution, configure {@link RequestMappingHandlerAdapter} directly. * @param argumentResolvers initially an empty list */ void addArgumentResolvers(List argumentResolvers); /** - * Add handlers to support custom controller method return value types. + * Add handlers to support custom controller method return value types. *

Using this option does not override the built-in support for handling - * return values. To customize the built-in support for handling return + * return values. To customize the built-in support for handling return * values, configure RequestMappingHandlerAdapter directly. * @param returnValueHandlers initially an empty list */ void addReturnValueHandlers(List returnValueHandlers); /** - * Configure the {@link HandlerExceptionResolver}s to handle unresolved + * Configure the {@link HandlerExceptionResolver}s to handle unresolved * controller exceptions. If no resolvers are added to the list, default * exception resolvers are added instead. * @param exceptionResolvers initially an empty list @@ -95,29 +96,36 @@ public interface WebMvcConfigurer { void configureHandlerExceptionResolvers(List exceptionResolvers); /** - * Add Spring MVC lifecycle interceptors for pre- and post-processing of - * controller method invocations. Interceptors can be registered to apply + * Add Spring MVC lifecycle interceptors for pre- and post-processing of + * controller method invocations. Interceptors can be registered to apply * to all requests or be limited to a subset of URL patterns. */ void addInterceptors(InterceptorRegistry registry); /** - * Add view controllers to create a direct mapping between a URL path and - * view name without the need for a controller in between. + * Provide a custom {@link MessageCodesResolver} for building message codes + * from data binding and validation error codes. Leave the return value as + * {@code null} to keep the default. + */ + MessageCodesResolver getMessageCodesResolver(); + + /** + * Add view controllers to create a direct mapping between a URL path and + * view name without the need for a controller in between. */ void addViewControllers(ViewControllerRegistry registry); /** - * Add handlers to serve static resources such as images, js, and, css + * Add handlers to serve static resources such as images, js, and, css * files from specific locations under web application root, the classpath, * and others. */ void addResourceHandlers(ResourceHandlerRegistry registry); /** - * Configure a handler to delegate unhandled requests by forwarding to the + * Configure a handler to delegate unhandled requests by forwarding to the * Servlet container's "default" servlet. A common use case for this is when - * the {@link DispatcherServlet} is mapped to "/" thus overriding the + * the {@link DispatcherServlet} is mapped to "/" thus overriding the * Servlet container's default handling of static resources. */ void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java index 6f941eca5af5..b64173fa1ab0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -20,13 +20,15 @@ import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.validation.MessageCodesResolver; 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 convenient base class with empty method implementations of {@link WebMvcConfigurer}. + * An implementation of {@link WebMvcConfigurer} with empty methods allowing + * sub-classes to override only the methods they're interested in. * * @author Rossen Stoyanchev * @since 3.1 @@ -76,6 +78,14 @@ public void addReturnValueHandlers(List returnV public void configureHandlerExceptionResolvers(List exceptionResolvers) { } + /** + * {@inheritDoc} + *

This implementation is empty. + */ + public MessageCodesResolver getMessageCodesResolver() { + return null; + } + /** * {@inheritDoc} *

This implementation is empty. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java index 1c0e7494e9a5..9cef9ee68b29 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -17,12 +17,11 @@ package org.springframework.web.servlet.config.annotation; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; @@ -45,77 +44,92 @@ public void addWebMvcConfigurers(List configurers) { } public void addFormatters(FormatterRegistry registry) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addFormatters(registry); } } public void configureMessageConverters(List> converters) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.configureMessageConverters(converters); } } public void addArgumentResolvers(List argumentResolvers) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addArgumentResolvers(argumentResolvers); } } public void addReturnValueHandlers(List returnValueHandlers) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addReturnValueHandlers(returnValueHandlers); } } public void configureHandlerExceptionResolvers(List exceptionResolvers) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.configureHandlerExceptionResolvers(exceptionResolvers); } } public void addInterceptors(InterceptorRegistry registry) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addInterceptors(registry); } } public void addViewControllers(ViewControllerRegistry registry) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addViewControllers(registry); } } public void addResourceHandlers(ResourceHandlerRegistry registry) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.addResourceHandlers(registry); } } public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - for (WebMvcConfigurer delegate : delegates) { + for (WebMvcConfigurer delegate : this.delegates) { delegate.configureDefaultServletHandling(configurer); } } public Validator getValidator() { - Map validators = new HashMap(); - for (WebMvcConfigurer delegate : delegates) { - Validator validator = delegate.getValidator(); + List candidates = new ArrayList(); + for (WebMvcConfigurer configurer : this.delegates) { + Validator validator = configurer.getValidator(); if (validator != null) { - validators.put(delegate, validator); + candidates.add(validator); } } - if (validators.size() == 0) { - return null; + return selectSingleInstance(candidates, Validator.class); + } + + private T selectSingleInstance(List instances, Class instanceType) { + if (instances.size() > 1) { + throw new IllegalStateException( + "Only one [" + instanceType + "] was expected but multiple instances were provided: " + instances); } - else if (validators.size() == 1) { - return validators.values().iterator().next(); + else if (instances.size() == 1) { + return instances.get(0); } else { - throw new IllegalStateException( - "Multiple custom validators provided from [" + validators.keySet() + "]"); + return null; + } + } + + public MessageCodesResolver getMessageCodesResolver() { + List candidates = new ArrayList(); + for (WebMvcConfigurer configurer : this.delegates) { + MessageCodesResolver messageCodesResolver = configurer.getMessageCodesResolver(); + if (messageCodesResolver != null) { + candidates.add(messageCodesResolver); + } } + return selectSingleInstance(candidates, MessageCodesResolver.class); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java index 2ce7795f76da..58e8302dd3b5 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2012 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. @@ -35,6 +35,7 @@ import org.springframework.format.support.FormattingConversionService; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -72,6 +73,7 @@ public void requestMappingHandlerAdapter() throws Exception { configurer.configureMessageConverters(capture(converters)); expect(configurer.getValidator()).andReturn(null); + expect(configurer.getMessageCodesResolver()).andReturn(null); configurer.addFormatters(capture(conversionService)); configurer.addArgumentResolvers(capture(resolvers)); configurer.addReturnValueHandlers(capture(handlers)); @@ -91,7 +93,7 @@ public void requestMappingHandlerAdapter() throws Exception { verify(configurer); } - @Test + @Test public void configureMessageConverters() { List configurers = new ArrayList(); configurers.add(new WebMvcConfigurerAdapter() { @@ -102,11 +104,11 @@ public void configureMessageConverters(List> converters) }); mvcConfiguration = new DelegatingWebMvcConfiguration(); mvcConfiguration.setConfigurers(configurers); - + RequestMappingHandlerAdapter 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()); @@ -118,6 +120,17 @@ public void getCustomValidator() { verify(configurer); } + @Test + public void getCustomMessageCodesResolver() { + expect(configurer.getMessageCodesResolver()).andReturn(new DefaultMessageCodesResolver()); + replay(configurer); + + mvcConfiguration.setConfigurers(Arrays.asList(configurer)); + mvcConfiguration.getMessageCodesResolver(); + + verify(configurer); + } + @Test public void handlerExceptionResolver() throws Exception { Capture>> converters = new Capture>>(); @@ -139,7 +152,7 @@ public void handlerExceptionResolver() throws Exception { verify(configurer); } - @Test + @Test public void configureExceptionResolvers() throws Exception { List configurers = new ArrayList(); configurers.add(new WebMvcConfigurerAdapter() { @@ -149,10 +162,10 @@ public void configureHandlerExceptionResolvers(List ex } }); mvcConfiguration.setConfigurers(configurers); - - HandlerExceptionResolverComposite composite = + + HandlerExceptionResolverComposite composite = (HandlerExceptionResolverComposite) mvcConfiguration.handlerExceptionResolver(); assertEquals("Only one custom converter is expected", 1, composite.getExceptionResolvers().size()); } - + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java index 8d1c8c8112a1..7edf796ade07 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java @@ -40,7 +40,9 @@ import org.springframework.mock.web.MockServletContext; import org.springframework.stereotype.Controller; import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.Errors; +import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.bind.annotation.RequestMapping; @@ -190,6 +192,9 @@ public void webMvcConfigurerExtensionHooks() throws Exception { initializer.getValidator().validate(null, bindingResult); assertEquals("invalid", bindingResult.getAllErrors().get(0).getCode()); + String[] codes = initializer.getMessageCodesResolver().resolveMessageCodes("invalid", null); + assertEquals("custom.invalid", codes[0]); + @SuppressWarnings("unchecked") List argResolvers= (List) new DirectFieldAccessor(adapter).getPropertyValue("customArgumentResolvers"); @@ -248,8 +253,11 @@ private static class TestWebMvcConfiguration extends WebMvcConfigurationSupport } /** - * The purpose of this class is to test that an implementation of a {@link WebMvcConfigurer} - * can also apply customizations by extension from {@link WebMvcConfigurationSupport}. + * Since WebMvcConfigurationSupport does not implement WebMvcConfigurer, the purpose + * of this test class is also to ensure the two are in sync with each other. Effectively + * that ensures that application config classes that use the combo {@code @EnableWebMvc} + * plus WebMvcConfigurer can switch to extending WebMvcConfigurationSupport directly for + * more advanced configuration needs. */ private class WebConfig extends WebMvcConfigurationSupport implements WebMvcConfigurer { @@ -299,6 +307,17 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LocaleChangeInterceptor()); } + @SuppressWarnings("serial") + @Override + public MessageCodesResolver getMessageCodesResolver() { + return new DefaultMessageCodesResolver() { + @Override + public String[] resolveMessageCodes(String errorCode, String objectName) { + return new String[] { "custom." + errorCode }; + } + }; + } + @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/path"); @@ -313,6 +332,7 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable("default"); } + } } diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 2c089eddf223..fd36db3ad90b 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -25,6 +25,7 @@ Changes in version 3.2 M1 * support access to all URI vars via @PathVariable Map * add "excludedExceptions" property to SimpleUrlHandlerMapping * add CompositeRequestCondition for use with multiple custom request mapping conditions +* add option to configure custom MessageCodesResolver through WebMvcConfigurer Changes in version 3.1.1 (2012-02-16) -------------------------------------