mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-23 06:29:10 +00:00
ConversionService detects generic type declaration on target class behind proxy as well
Issue: SPR-14822
This commit is contained in:
+7
-2
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
import org.springframework.context.EmbeddedValueResolverAware;
|
import org.springframework.context.EmbeddedValueResolverAware;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
import org.springframework.core.DecoratingProxy;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
@@ -97,9 +98,13 @@ public class FormattingConversionService extends GenericConversionService
|
|||||||
|
|
||||||
static Class<?> getFieldType(Formatter<?> formatter) {
|
static Class<?> getFieldType(Formatter<?> formatter) {
|
||||||
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
|
||||||
|
if (fieldType == null && formatter instanceof DecoratingProxy) {
|
||||||
|
fieldType = GenericTypeResolver.resolveTypeArgument(
|
||||||
|
((DecoratingProxy) formatter).getDecoratedClass(), Formatter.class);
|
||||||
|
}
|
||||||
if (fieldType == null) {
|
if (fieldType == null) {
|
||||||
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" +
|
throw new IllegalArgumentException("Unable to extract the parameterized field type from Formatter [" +
|
||||||
formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
|
formatter.getClass().getName() + "]; does the class parameterize the <T> generic type?");
|
||||||
}
|
}
|
||||||
return fieldType;
|
return fieldType;
|
||||||
}
|
}
|
||||||
|
|||||||
+96
-24
@@ -32,7 +32,7 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
import org.springframework.beans.ConfigurablePropertyAccessor;
|
import org.springframework.beans.ConfigurablePropertyAccessor;
|
||||||
import org.springframework.beans.PropertyAccessorFactory;
|
import org.springframework.beans.PropertyAccessorFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -45,6 +45,7 @@ import org.springframework.context.support.GenericApplicationContext;
|
|||||||
import org.springframework.core.convert.ConversionFailedException;
|
import org.springframework.core.convert.ConversionFailedException;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.ConverterFactory;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.format.Formatter;
|
import org.springframework.format.Formatter;
|
||||||
import org.springframework.format.Printer;
|
import org.springframework.format.Printer;
|
||||||
@@ -81,7 +82,7 @@ public class FormattingConversionServiceTests {
|
|||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatFieldForTypeWithFormatter() throws ParseException {
|
public void formatFieldForTypeWithFormatter() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
||||||
String formatted = formattingService.convert(3, String.class);
|
String formatted = formattingService.convert(3, String.class);
|
||||||
assertEquals("3", formatted);
|
assertEquals("3", formatted);
|
||||||
@@ -90,7 +91,7 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatFieldForTypeWithPrinterParserWithCoercion() throws ParseException {
|
public void formatFieldForTypeWithPrinterParserWithCoercion() throws ParseException {
|
||||||
formattingService.addConverter(new Converter<DateTime, LocalDate>() {
|
formattingService.addConverter(new Converter<DateTime, LocalDate>() {
|
||||||
@Override
|
@Override
|
||||||
public LocalDate convert(DateTime source) {
|
public LocalDate convert(DateTime source) {
|
||||||
@@ -107,7 +108,7 @@ public class FormattingConversionServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public void testFormatFieldForValueInjection() {
|
public void formatFieldForValueInjection() {
|
||||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||||
ac.registerBeanDefinition("valueBean", new RootBeanDefinition(ValueBean.class));
|
ac.registerBeanDefinition("valueBean", new RootBeanDefinition(ValueBean.class));
|
||||||
ac.registerBeanDefinition("conversionService", new RootBeanDefinition(FormattingConversionServiceFactoryBean.class));
|
ac.registerBeanDefinition("conversionService", new RootBeanDefinition(FormattingConversionServiceFactoryBean.class));
|
||||||
@@ -118,7 +119,7 @@ public class FormattingConversionServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public void testFormatFieldForValueInjectionUsingMetaAnnotations() {
|
public void formatFieldForValueInjectionUsingMetaAnnotations() {
|
||||||
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
|
||||||
RootBeanDefinition bd = new RootBeanDefinition(MetaValueBean.class);
|
RootBeanDefinition bd = new RootBeanDefinition(MetaValueBean.class);
|
||||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
@@ -140,20 +141,20 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatFieldForAnnotation() throws Exception {
|
public void formatFieldForAnnotation() throws Exception {
|
||||||
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
|
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
|
||||||
doTestFormatFieldForAnnotation(Model.class, false);
|
doTestFormatFieldForAnnotation(Model.class, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatFieldForAnnotationWithDirectFieldAccess() throws Exception {
|
public void formatFieldForAnnotationWithDirectFieldAccess() throws Exception {
|
||||||
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
|
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory());
|
||||||
doTestFormatFieldForAnnotation(Model.class, true);
|
doTestFormatFieldForAnnotation(Model.class, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public void testFormatFieldForAnnotationWithPlaceholders() throws Exception {
|
public void formatFieldForAnnotationWithPlaceholders() throws Exception {
|
||||||
GenericApplicationContext context = new GenericApplicationContext();
|
GenericApplicationContext context = new GenericApplicationContext();
|
||||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
@@ -169,7 +170,7 @@ public class FormattingConversionServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
public void testFormatFieldForAnnotationWithPlaceholdersAndFactoryBean() throws Exception {
|
public void formatFieldForAnnotationWithPlaceholdersAndFactoryBean() throws Exception {
|
||||||
GenericApplicationContext context = new GenericApplicationContext();
|
GenericApplicationContext context = new GenericApplicationContext();
|
||||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
@@ -239,61 +240,61 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPrintNull() throws ParseException {
|
public void printNull() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
||||||
assertEquals("", formattingService.convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)));
|
assertEquals("", formattingService.convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseNull() throws ParseException {
|
public void parseNull() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
||||||
assertNull(formattingService
|
assertNull(formattingService
|
||||||
.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseEmptyString() throws ParseException {
|
public void parseEmptyString() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
||||||
assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseBlankString() throws ParseException {
|
public void parseBlankString() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Number.class, new NumberStyleFormatter());
|
||||||
assertNull(formattingService.convert(" ", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
assertNull(formattingService.convert(" ", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = ConversionFailedException.class)
|
@Test(expected = ConversionFailedException.class)
|
||||||
public void testParseParserReturnsNull() throws ParseException {
|
public void parseParserReturnsNull() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Integer.class, new NullReturningFormatter());
|
formattingService.addFormatterForFieldType(Integer.class, new NullReturningFormatter());
|
||||||
assertNull(formattingService.convert("1", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
assertNull(formattingService.convert("1", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = ConversionFailedException.class)
|
@Test(expected = ConversionFailedException.class)
|
||||||
public void testParseNullPrimitiveProperty() throws ParseException {
|
public void parseNullPrimitiveProperty() throws ParseException {
|
||||||
formattingService.addFormatterForFieldType(Integer.class, new NumberStyleFormatter());
|
formattingService.addFormatterForFieldType(Integer.class, new NumberStyleFormatter());
|
||||||
assertNull(formattingService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(int.class)));
|
assertNull(formattingService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(int.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPrintNullDefault() throws ParseException {
|
public void printNullDefault() throws ParseException {
|
||||||
assertEquals(null, formattingService
|
assertEquals(null, formattingService
|
||||||
.convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)));
|
.convert(null, TypeDescriptor.valueOf(Integer.class), TypeDescriptor.valueOf(String.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseNullDefault() throws ParseException {
|
public void parseNullDefault() throws ParseException {
|
||||||
assertNull(formattingService
|
assertNull(formattingService
|
||||||
.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseEmptyStringDefault() throws ParseException {
|
public void parseEmptyStringDefault() throws ParseException {
|
||||||
assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
assertNull(formattingService.convert("", TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatFieldForAnnotationWithSubclassAsFieldType() throws Exception {
|
public void formatFieldForAnnotationWithSubclassAsFieldType() throws Exception {
|
||||||
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory() {
|
formattingService.addFormatterForFieldAnnotation(new JodaDateTimeFormatAnnotationFormatterFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Printer<?> getPrinter(org.springframework.format.annotation.DateTimeFormat annotation, Class<?> fieldType) {
|
public Printer<?> getPrinter(org.springframework.format.annotation.DateTimeFormat annotation, Class<?> fieldType) {
|
||||||
@@ -319,7 +320,7 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisterDefaultValueViaFormatter() {
|
public void registerDefaultValueViaFormatter() {
|
||||||
registerDefaultValue(Date.class, new Date());
|
registerDefaultValue(Date.class, new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +341,45 @@ public class FormattingConversionServiceTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void introspectedFormatter() throws ParseException {
|
||||||
|
formattingService.addFormatter(new NumberStyleFormatter());
|
||||||
|
assertNull(formattingService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void proxiedFormatter() throws ParseException {
|
||||||
|
Formatter<?> formatter = new NumberStyleFormatter();
|
||||||
|
formattingService.addFormatter((Formatter<?>) new ProxyFactory(formatter).getProxy());
|
||||||
|
assertNull(formattingService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void introspectedConverter() {
|
||||||
|
formattingService.addConverter(new IntegerConverter());
|
||||||
|
assertEquals(Integer.valueOf(1), formattingService.convert("1", Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void proxiedConverter() {
|
||||||
|
Converter<?, ?> converter = new IntegerConverter();
|
||||||
|
formattingService.addConverter((Converter<?, ?>) new ProxyFactory(converter).getProxy());
|
||||||
|
assertEquals(Integer.valueOf(1), formattingService.convert("1", Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void introspectedConverterFactory() {
|
||||||
|
formattingService.addConverterFactory(new IntegerConverterFactory());
|
||||||
|
assertEquals(Integer.valueOf(1), formattingService.convert("1", Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void proxiedConverterFactory() {
|
||||||
|
ConverterFactory<?, ?> converterFactory = new IntegerConverterFactory();
|
||||||
|
formattingService.addConverterFactory((ConverterFactory<?, ?>) new ProxyFactory(converterFactory).getProxy());
|
||||||
|
assertEquals(Integer.valueOf(1), formattingService.convert("1", Integer.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class ValueBean {
|
public static class ValueBean {
|
||||||
|
|
||||||
@@ -348,6 +388,7 @@ public class FormattingConversionServiceTests {
|
|||||||
public Date date;
|
public Date date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class MetaValueBean {
|
public static class MetaValueBean {
|
||||||
|
|
||||||
@MyDateAnn
|
@MyDateAnn
|
||||||
@@ -357,18 +398,21 @@ public class FormattingConversionServiceTests {
|
|||||||
public Double number;
|
public Double number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Value("${myDate}")
|
@Value("${myDate}")
|
||||||
@org.springframework.format.annotation.DateTimeFormat(pattern="MM-d-yy")
|
@org.springframework.format.annotation.DateTimeFormat(pattern="MM-d-yy")
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public static @interface MyDateAnn {
|
public @interface MyDateAnn {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Value("${myNumber}")
|
@Value("${myNumber}")
|
||||||
@NumberFormat(style = NumberFormat.Style.PERCENT)
|
@NumberFormat(style = NumberFormat.Style.PERCENT)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public static @interface MyNumberAnn {
|
public @interface MyNumberAnn {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Model {
|
public static class Model {
|
||||||
|
|
||||||
@org.springframework.format.annotation.DateTimeFormat(style="S-")
|
@org.springframework.format.annotation.DateTimeFormat(style="S-")
|
||||||
@@ -386,6 +430,7 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class ModelWithPlaceholders {
|
public static class ModelWithPlaceholders {
|
||||||
|
|
||||||
@org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}")
|
@org.springframework.format.annotation.DateTimeFormat(style="${dateStyle}")
|
||||||
@@ -403,11 +448,13 @@ public class FormattingConversionServiceTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}")
|
@org.springframework.format.annotation.DateTimeFormat(pattern="${datePattern}")
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public static @interface MyDatePattern {
|
public @interface MyDatePattern {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class NullReturningFormatter implements Formatter<Integer> {
|
public static class NullReturningFormatter implements Formatter<Integer> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -419,17 +466,42 @@ public class FormattingConversionServiceTests {
|
|||||||
public Integer parse(String text, Locale locale) throws ParseException {
|
public Integer parse(String text, Locale locale) throws ParseException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class MyDate extends Date {
|
public static class MyDate extends Date {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class ModelWithSubclassField {
|
private static class ModelWithSubclassField {
|
||||||
|
|
||||||
@org.springframework.format.annotation.DateTimeFormat(style = "S-")
|
@org.springframework.format.annotation.DateTimeFormat(style = "S-")
|
||||||
public MyDate date;
|
public MyDate date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class IntegerConverter implements Converter<String, Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer convert(String source) {
|
||||||
|
return Integer.parseInt(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class IntegerConverterFactory implements ConverterFactory<String, Number> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
|
||||||
|
if (Integer.class == targetType) {
|
||||||
|
return (Converter<String, T>) new IntegerConverter();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2015 the original author or authors.
|
* Copyright 2002-2016 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -34,7 +34,7 @@ public interface ConverterFactory<S, R> {
|
|||||||
* Get the converter to convert from S to target type T, where T is also an instance of R.
|
* Get the converter to convert from S to target type T, where T is also an instance of R.
|
||||||
* @param <T> the target type
|
* @param <T> the target type
|
||||||
* @param targetType the target type to convert to
|
* @param targetType the target type to convert to
|
||||||
* @return A converter from S to T
|
* @return a converter from S to T
|
||||||
*/
|
*/
|
||||||
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
|
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -49,12 +49,12 @@ public interface ConverterRegistry {
|
|||||||
/**
|
/**
|
||||||
* Add a ranged converter factory to this registry.
|
* Add a ranged converter factory to this registry.
|
||||||
* The convertible source/target type pair is derived from the ConverterFactory's parameterized types.
|
* The convertible source/target type pair is derived from the ConverterFactory's parameterized types.
|
||||||
* @throws IllegalArgumentException if the parameterized types could not be resolved.
|
* @throws IllegalArgumentException if the parameterized types could not be resolved
|
||||||
*/
|
*/
|
||||||
void addConverterFactory(ConverterFactory<?, ?> converterFactory);
|
void addConverterFactory(ConverterFactory<?, ?> factory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove any converters from sourceType to targetType.
|
* Remove any converters from {@code sourceType} to {@code targetType}.
|
||||||
* @param sourceType the source type
|
* @param sourceType the source type
|
||||||
* @param targetType the target type
|
* @param targetType the target type
|
||||||
*/
|
*/
|
||||||
|
|||||||
+21
-10
@@ -28,6 +28,7 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.core.DecoratingProxy;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.convert.ConversionException;
|
import org.springframework.core.convert.ConversionException;
|
||||||
import org.springframework.core.convert.ConversionFailedException;
|
import org.springframework.core.convert.ConversionFailedException;
|
||||||
@@ -83,9 +84,14 @@ public class GenericConversionService implements ConfigurableConversionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConverter(Converter<?, ?> converter) {
|
public void addConverter(Converter<?, ?> converter) {
|
||||||
ResolvableType[] typeInfo = getRequiredTypeInfo(converter, Converter.class);
|
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
|
||||||
Assert.notNull(typeInfo, "Unable to the determine sourceType <S> and targetType " +
|
if (typeInfo == null && converter instanceof DecoratingProxy) {
|
||||||
"<T> which your Converter<S, T> converts between; declare these generic types.");
|
typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
|
||||||
|
}
|
||||||
|
if (typeInfo == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
|
||||||
|
"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
|
||||||
|
}
|
||||||
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
|
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,11 +108,16 @@ public class GenericConversionService implements ConfigurableConversionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
|
public void addConverterFactory(ConverterFactory<?, ?> factory) {
|
||||||
ResolvableType[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class);
|
ResolvableType[] typeInfo = getRequiredTypeInfo(factory.getClass(), ConverterFactory.class);
|
||||||
Assert.notNull(typeInfo, "Unable to the determine source type <S> and target range type R which your " +
|
if (typeInfo == null && factory instanceof DecoratingProxy) {
|
||||||
"ConverterFactory<S, R> converts between; declare these generic types.");
|
typeInfo = getRequiredTypeInfo(((DecoratingProxy) factory).getDecoratedClass(), ConverterFactory.class);
|
||||||
addConverter(new ConverterFactoryAdapter(converterFactory,
|
}
|
||||||
|
if (typeInfo == null) {
|
||||||
|
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
|
||||||
|
"ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?");
|
||||||
|
}
|
||||||
|
addConverter(new ConverterFactoryAdapter(factory,
|
||||||
new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve())));
|
new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,8 +282,8 @@ public class GenericConversionService implements ConfigurableConversionService {
|
|||||||
|
|
||||||
// Internal helpers
|
// Internal helpers
|
||||||
|
|
||||||
private ResolvableType[] getRequiredTypeInfo(Object converter, Class<?> genericIfc) {
|
private ResolvableType[] getRequiredTypeInfo(Class<?> converterClass, Class<?> genericIfc) {
|
||||||
ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc);
|
ResolvableType resolvableType = ResolvableType.forClass(converterClass).as(genericIfc);
|
||||||
ResolvableType[] generics = resolvableType.getGenerics();
|
ResolvableType[] generics = resolvableType.getGenerics();
|
||||||
if (generics.length < 2) {
|
if (generics.length < 2) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user