From e904589bd1f910b095acdd1fb760a76ab84e8e54 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 4 Sep 2012 22:36:12 +0200 Subject: [PATCH] added Field context variant to TypeConverter interface in beans module; @Value injection works in combination with formatting rules such as @DateTimeFormat Issue: SPR-9637 --- .../beans/AbstractPropertyAccessor.java | 11 +-- .../beans/BeanWrapperImpl.java | 22 +----- .../beans/DirectFieldAccessor.java | 30 ++----- .../beans/SimpleTypeConverter.java | 44 ++--------- .../springframework/beans/TypeConverter.java | 38 ++++++--- .../beans/TypeConverterDelegate.java | 17 ++++ .../beans/TypeConverterSupport.java | 78 +++++++++++++++++++ .../support/DefaultListableBeanFactory.java | 4 +- .../DefaultListableBeanFactoryTests.java | 35 ++++----- .../validation/DataBinder.java | 13 +++- .../FormattingConversionServiceTests.java | 24 +++++- 11 files changed, 195 insertions(+), 121 deletions(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index 6f06889a244..c72277d2cf2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -31,8 +31,7 @@ import java.util.Map; * @see #getPropertyValue * @see #setPropertyValue */ -public abstract class AbstractPropertyAccessor extends PropertyEditorRegistrySupport - implements ConfigurablePropertyAccessor { +public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor { private boolean extractOldValueForEditor = false; @@ -103,12 +102,8 @@ public abstract class AbstractPropertyAccessor extends PropertyEditorRegistrySup } } - public T convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException { - return convertIfNecessary(value, requiredType, null); - } - - // Redefined with public visibility. + // Redefined with public visibility. @Override public Class getPropertyType(String propertyPath) { return null; diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 730f593bf63..efe6fccbcc2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -19,6 +19,7 @@ package org.springframework.beans; import java.beans.PropertyChangeEvent; import java.beans.PropertyDescriptor; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -100,8 +101,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra private Object rootObject; - private TypeConverterDelegate typeConverterDelegate; - /** * The security context used for invoking the property methods */ @@ -445,25 +444,6 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra return false; } - public T convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) - throws TypeMismatchException { - try { - return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); - } - catch (ConverterNotFoundException ex) { - throw new ConversionNotSupportedException(value, requiredType, ex); - } - catch (ConversionException ex) { - throw new TypeMismatchException(value, requiredType, ex); - } - catch (IllegalStateException ex) { - throw new ConversionNotSupportedException(value, requiredType, ex); - } - catch (IllegalArgumentException ex) { - throw new TypeMismatchException(value, requiredType, ex); - } - } - private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class requiredType, TypeDescriptor td) throws TypeMismatchException { try { diff --git a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java index d1fb04ef435..caaa069b066 100644 --- a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.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. @@ -16,11 +16,6 @@ package org.springframework.beans; -import java.beans.PropertyChangeEvent; -import java.lang.reflect.Field; -import java.util.HashMap; -import java.util.Map; - import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConverterNotFoundException; @@ -28,6 +23,11 @@ import org.springframework.core.convert.TypeDescriptor; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; +import java.beans.PropertyChangeEvent; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + /** * {@link PropertyAccessor} implementation that directly accesses instance fields. * Allows for direct binding to fields instead of going through JavaBean setters. @@ -51,8 +51,6 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor { private final Map fieldMap = new HashMap(); - private final TypeConverterDelegate typeConverterDelegate; - /** * Create a new DirectFieldAccessor for the given target object. @@ -65,7 +63,8 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor { public void doWith(Field field) { if (fieldMap.containsKey(field.getName())) { // ignore superclass declarations of fields already found in a subclass - } else { + } + else { fieldMap.put(field.getName(), field); } } @@ -153,17 +152,4 @@ public class DirectFieldAccessor extends AbstractPropertyAccessor { } } - public T convertIfNecessary( - Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { - try { - return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); - } - catch (IllegalArgumentException ex) { - throw new TypeMismatchException(value, requiredType, ex); - } - catch (IllegalStateException ex) { - throw new ConversionNotSupportedException(value, requiredType, ex); - } - } - } diff --git a/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java b/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java index a75e5fd3625..1e074dcc338 100644 --- a/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java +++ b/spring-beans/src/main/java/org/springframework/beans/SimpleTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 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. @@ -16,50 +16,22 @@ package org.springframework.beans; -import org.springframework.core.MethodParameter; -import org.springframework.core.convert.ConversionException; -import org.springframework.core.convert.ConverterNotFoundException; - /** - * Simple implementation of the TypeConverter interface that does not operate - * on any specific target object. This is an alternative to using a full-blown - * BeanWrapperImpl instance for arbitrary type conversion needs. + * Simple implementation of the {@link TypeConverter} interface that does not operate on + * a specific target object. This is an alternative to using a full-blown BeanWrapperImpl + * instance for arbitrary type conversion needs, while using the very same conversion + * algorithm (including delegation to {@link java.beans.PropertyEditor} and + * {@link org.springframework.core.convert.ConversionService}) underneath. * * @author Juergen Hoeller * @since 2.0 * @see BeanWrapperImpl */ -public class SimpleTypeConverter extends PropertyEditorRegistrySupport implements TypeConverter { - - private final TypeConverterDelegate typeConverterDelegate = new TypeConverterDelegate(this); - +public class SimpleTypeConverter extends TypeConverterSupport { public SimpleTypeConverter() { + this.typeConverterDelegate = new TypeConverterDelegate(this); registerDefaultEditors(); } - - public T convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException { - return convertIfNecessary(value, requiredType, null); - } - - public T convertIfNecessary( - Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { - try { - return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); - } - catch (ConverterNotFoundException ex) { - throw new ConversionNotSupportedException(value, requiredType, ex); - } - catch (ConversionException ex) { - throw new TypeMismatchException(value, requiredType, ex); - } - catch (IllegalStateException ex) { - throw new ConversionNotSupportedException(value, requiredType, ex); - } - catch (IllegalArgumentException ex) { - throw new TypeMismatchException(value, requiredType, ex); - } - } - } diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java index 8aa36cf7f81..383f8e0d1e0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -16,15 +16,16 @@ package org.springframework.beans; +import java.lang.reflect.Field; + import org.springframework.core.MethodParameter; /** * Interface that defines type conversion methods. Typically (but not necessarily) - * implemented in conjunction with the PropertyEditorRegistry interface. + * implemented in conjunction with the {@link PropertyEditorRegistry} interface. * * @author Juergen Hoeller * @since 2.0 - * @see PropertyEditorRegistry * @see SimpleTypeConverter * @see BeanWrapperImpl */ @@ -33,9 +34,7 @@ public interface TypeConverter { /** * Convert the value to the required type (if necessary from a String). *

Conversions from String to any type will typically use the setAsText - * method of the PropertyEditor class. Note that a PropertyEditor must be registered - * for the given class for this to work; this is a standard JavaBeans API. - * A number of PropertyEditors are automatically registered. + * method of the PropertyEditor class, or a Spring Converter in a ConversionService. * @param value the value to convert * @param requiredType the type we must convert to * (or null if not known, for example in case of a collection element) @@ -43,15 +42,15 @@ public interface TypeConverter { * @throws TypeMismatchException if type conversion failed * @see java.beans.PropertyEditor#setAsText(String) * @see java.beans.PropertyEditor#getValue() + * @see org.springframework.core.convert.ConversionService + * @see org.springframework.core.convert.converter.Converter */ T convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException; /** * Convert the value to the required type (if necessary from a String). *

Conversions from String to any type will typically use the setAsText - * method of the PropertyEditor class. Note that a PropertyEditor must be registered - * for the given class for this to work; this is a standard JavaBeans API. - * A number of PropertyEditors are automatically registered. + * method of the PropertyEditor class, or a Spring Converter in a ConversionService. * @param value the value to convert * @param requiredType the type we must convert to * (or null if not known, for example in case of a collection element) @@ -61,8 +60,29 @@ public interface TypeConverter { * @throws TypeMismatchException if type conversion failed * @see java.beans.PropertyEditor#setAsText(String) * @see java.beans.PropertyEditor#getValue() + * @see org.springframework.core.convert.ConversionService + * @see org.springframework.core.convert.converter.Converter */ T convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException; + /** + * Convert the value to the required type (if necessary from a String). + *

Conversions from String to any type will typically use the setAsText + * method of the PropertyEditor class, or a Spring Converter in a ConversionService. + * @param value the value to convert + * @param requiredType the type we must convert to + * (or null if not known, for example in case of a collection element) + * @param field the reflective field that is the target of the conversion + * (for analysis of generic types; may be null) + * @return the new value, possibly the result of type conversion + * @throws TypeMismatchException if type conversion failed + * @see java.beans.PropertyEditor#setAsText(String) + * @see java.beans.PropertyEditor#getValue() + * @see org.springframework.core.convert.ConversionService + * @see org.springframework.core.convert.converter.Converter + */ + T convertIfNecessary(Object value, Class requiredType, Field field) + throws TypeMismatchException; + } diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 02f2c7e928a..b06166d13ef 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -94,6 +94,23 @@ class TypeConverterDelegate { (methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType))); } + /** + * Convert the value to the specified required type. + * @param newValue the proposed new value + * @param requiredType the type we must convert to + * (or null if not known, for example in case of a collection element) + * @param field the reflective field that is the target of the conversion + * (may be null) + * @return the new value, possibly the result of type conversion + * @throws IllegalArgumentException if type conversion failed + */ + public T convertIfNecessary(Object newValue, Class requiredType, Field field) + throws IllegalArgumentException { + + return convertIfNecessary(null, null, newValue, requiredType, + (field != null ? new TypeDescriptor(field) : TypeDescriptor.valueOf(requiredType))); + } + /** * Convert the value to the required type for the specified property. * @param propertyName name of the property diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java new file mode 100644 index 00000000000..61fb062afe5 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java @@ -0,0 +1,78 @@ +/* + * 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. + * 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.beans; + +import java.lang.reflect.Field; + +import org.springframework.core.MethodParameter; +import org.springframework.core.convert.ConversionException; +import org.springframework.core.convert.ConverterNotFoundException; + +/** + * Base implementation of the {@link TypeConverter} interface, using a package-private delegate. + * Mainly serves as base class for {@link BeanWrapperImpl}. + * + * @author Juergen Hoeller + * @since 3.2 + * @see SimpleTypeConverter + */ +public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter { + + TypeConverterDelegate typeConverterDelegate; + + + public T convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException { + return doConvert(value, requiredType, null, null); + } + + public T convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) + throws TypeMismatchException { + + return doConvert(value, requiredType, methodParam, null); + } + + public T convertIfNecessary(Object value, Class requiredType, Field field) + throws TypeMismatchException { + + return doConvert(value, requiredType, null, field); + } + + private T doConvert(Object value, Class requiredType, MethodParameter methodParam, Field field) + throws TypeMismatchException { + try { + if (field != null) { + return this.typeConverterDelegate.convertIfNecessary(value, requiredType, field); + } + else { + return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam); + } + } + catch (ConverterNotFoundException ex) { + throw new ConversionNotSupportedException(value, requiredType, ex); + } + catch (ConversionException ex) { + throw new TypeMismatchException(value, requiredType, ex); + } + catch (IllegalStateException ex) { + throw new ConversionNotSupportedException(value, requiredType, ex); + } + catch (IllegalArgumentException ex) { + throw new TypeMismatchException(value, requiredType, ex); + } + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index fe7d2ebdd5b..36b6a60b51a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -743,7 +743,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); - return converter.convertIfNecessary(value, type); + return (descriptor.getField() != null ? + converter.convertIfNecessary(value, type, descriptor.getField()) : + converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } if (type.isArray()) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index c123347482c..dc3fa8f8f5f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -17,17 +17,13 @@ package org.springframework.beans.factory; import java.lang.reflect.Field; - import java.net.MalformedURLException; - import java.security.AccessControlContext; import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; - import java.text.NumberFormat; import java.text.ParseException; - import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -35,14 +31,18 @@ import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; - import javax.security.auth.Subject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.junit.Ignore; import org.junit.Test; +import test.beans.DerivedTestBean; +import test.beans.DummyFactory; +import test.beans.ITestBean; +import test.beans.LifecycleBean; +import test.beans.NestedTestBean; +import test.beans.TestBean; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; @@ -79,15 +79,7 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.util.StopWatch; -import test.beans.DerivedTestBean; -import test.beans.DummyFactory; -import test.beans.ITestBean; -import test.beans.LifecycleBean; -import test.beans.NestedTestBean; -import test.beans.TestBean; - import static org.hamcrest.CoreMatchers.*; - import static org.junit.Assert.*; /** @@ -2432,11 +2424,6 @@ public class DefaultListableBeanFactoryTests { @SuppressWarnings("unchecked") public Object convertIfNecessary(Object value, Class requiredType) { - return convertIfNecessary(value, requiredType, null); - } - - @SuppressWarnings("unchecked") - public Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) { if (value instanceof String && Float.class.isAssignableFrom(requiredType)) { try { return new Float(this.numberFormat.parse((String) value).floatValue()); @@ -2452,6 +2439,16 @@ public class DefaultListableBeanFactoryTests { return value; } } + + @SuppressWarnings("unchecked") + public Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) { + return convertIfNecessary(value, requiredType); + } + + @SuppressWarnings("unchecked") + public Object convertIfNecessary(Object value, Class requiredType, Field field) { + return convertIfNecessary(value, requiredType); + } } diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 7758b7f4c93..973e3712ada 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.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,6 +17,7 @@ package org.springframework.validation; import java.beans.PropertyEditor; +import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; @@ -547,12 +548,18 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { return getTypeConverter().convertIfNecessary(value, requiredType); } - public T convertIfNecessary( - Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException { + public T convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam) + throws TypeMismatchException { return getTypeConverter().convertIfNecessary(value, requiredType, methodParam); } + public T convertIfNecessary(Object value, Class requiredType, Field field) + throws TypeMismatchException { + + return getTypeConverter().convertIfNecessary(value, requiredType, field); + } + /** * Bind the given property values to this binder's target. diff --git a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index 3c5a81f3785..eb640a497aa 100644 --- a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.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. @@ -33,8 +33,10 @@ import org.junit.Test; import org.springframework.beans.BeanUtils; import org.springframework.beans.ConfigurablePropertyAccessor; import org.springframework.beans.PropertyAccessorFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.convert.ConversionFailedException; @@ -94,6 +96,16 @@ public class FormattingConversionServiceTests { assertEquals(new LocalDate(2009, 10, 31), date); } + @Test + public void testFormatFieldForValueInjection() { + AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); + ac.registerBeanDefinition("valueBean", new RootBeanDefinition(ValueBean.class)); + ac.registerBeanDefinition("conversionService", new RootBeanDefinition(FormattingConversionServiceFactoryBean.class)); + ac.refresh(); + ValueBean valueBean = ac.getBean(ValueBean.class); + assertEquals(new LocalDate(2009, 10, 31), new LocalDate(valueBean.date)); + } + @Test public void testFormatFieldForAnnotation() throws Exception { formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory()); @@ -253,7 +265,7 @@ public class FormattingConversionServiceTests { }); formattingService.addConverter(new Converter() { public Long convert(MyDate source) { - return source.getTime(); + return source.getTime(); } }); formattingService.addConverter(new Converter() { @@ -267,6 +279,14 @@ public class FormattingConversionServiceTests { } + public static class ValueBean { + + @Value("10-31-09") + @org.springframework.format.annotation.DateTimeFormat(pattern="MM-d-yy") + public Date date; + } + + public static class Model { @org.springframework.format.annotation.DateTimeFormat(style="S-")