From e60389283dc8e1f32b1b358dafa807d3784448b1 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Mon, 19 Apr 2010 03:58:28 +0000 Subject: [PATCH] caching optmizations and performance tests --- .../core/convert/TypeDescriptor.java | 165 ++++++++++-------- .../GenericConversionServiceTests.java | 64 ++++++- 2 files changed, 155 insertions(+), 74 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 4585a70d23..a2461428df 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -73,6 +73,12 @@ public class TypeDescriptor { private Object value; + private TypeDescriptor elementType; + + private TypeDescriptor mapKeyType; + + private TypeDescriptor mapValueType; + /** * Create a new type descriptor from a method or constructor parameter. *

Use this constructor when a target conversion point originates from a method parameter, @@ -233,22 +239,19 @@ public class TypeDescriptor { * Returns null if the type is neither an array or collection. */ public Class getElementType() { - if (isArray()) { - return getArrayComponentType(); - } - else if (isCollection()) { - return getCollectionElementType(); - } - else { - return null; - } + return getElementTypeDescriptor().getType(); } /** * Return the element type as a type descriptor. */ public TypeDescriptor getElementTypeDescriptor() { - return forElementType(getElementType()); + if (elementType != null) { + return elementType; + } else { + elementType = forElementType(resolveElementType()); + return elementType; + } } /** @@ -257,7 +260,8 @@ public class TypeDescriptor { * @return the element type descriptor */ public TypeDescriptor getElementTypeDescriptor(Object element) { - return getElementType() != null ? getElementTypeDescriptor() : TypeDescriptor.forObject(element); + TypeDescriptor elementType = getElementTypeDescriptor(); + return elementType != TypeDescriptor.NULL ? elementType : TypeDescriptor.forObject(element); } /** @@ -278,63 +282,20 @@ public class TypeDescriptor { * Determine the generic key type of the wrapped Map parameter/field, if any. * @return the generic type, or null if none */ - @SuppressWarnings("unchecked") public Class getMapKeyType() { - if (isMap()) { - if (this.field != null) { - return GenericCollectionTypeResolver.getMapKeyFieldType(this.field); - } - else if (this.methodParameter != null) { - return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter); - } - else if (this.value instanceof Map) { - Map map = (Map) this.value; - if (!map.isEmpty()) { - Object key = map.keySet().iterator().next(); - if (key != null) { - return key.getClass(); - } - } - } - return GenericCollectionTypeResolver.getMapKeyType((Class) this.type); - } else { - return null; - } - } - - /** - * Determine the generic value type of the wrapped Map parameter/field, if any. - * @return the generic type, or null if none - */ - @SuppressWarnings("unchecked") - public Class getMapValueType() { - if (isMap()) { - if (this.field != null) { - return GenericCollectionTypeResolver.getMapValueFieldType(this.field); - } - else if (this.methodParameter != null) { - return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter); - } - else if (this.value instanceof Map) { - Map map = (Map) this.value; - if (!map.isEmpty()) { - Object val = map.values().iterator().next(); - if (val != null) { - return val.getClass(); - } - } - } - return GenericCollectionTypeResolver.getMapValueType((Class) this.type); - } else { - return null; - } + return getMapKeyTypeDescriptor().getType(); } /** * Returns map key type as a type descriptor. */ public TypeDescriptor getMapKeyTypeDescriptor() { - return forElementType(getMapKeyType()); + if (mapKeyType != null) { + return mapKeyType; + } else { + mapKeyType = isMap() ? forElementType(resolveMapKeyType()) : null; + return mapKeyType; + } } /** @@ -343,14 +304,28 @@ public class TypeDescriptor { * @return the map key type descriptor */ public TypeDescriptor getMapKeyTypeDescriptor(Object key) { - return getMapKeyType() != null ? getMapKeyTypeDescriptor() : TypeDescriptor.forObject(key); + TypeDescriptor keyType = getMapKeyTypeDescriptor(); + return keyType != TypeDescriptor.NULL ? keyType : TypeDescriptor.forObject(key); + } + + /** + * Determine the generic value type of the wrapped Map parameter/field, if any. + * @return the generic type, or null if none + */ + public Class getMapValueType() { + return getMapValueTypeDescriptor().getType(); } /** * Returns map value type as a type descriptor. */ public TypeDescriptor getMapValueTypeDescriptor() { - return forElementType(getMapValueType()); + if (mapValueType != null) { + return mapValueType; + } else { + mapValueType = isMap() ? forElementType(resolveMapValueType()) : null; + return mapValueType; + } } /** @@ -359,7 +334,8 @@ public class TypeDescriptor { * @return the map value type descriptor */ public TypeDescriptor getMapValueTypeDescriptor(Object value) { - return getMapValueType() != null ? getMapValueTypeDescriptor() : TypeDescriptor.forObject(value); + TypeDescriptor valueType = getMapValueTypeDescriptor(); + return valueType != TypeDescriptor.NULL ? valueType : TypeDescriptor.forObject(value); } /** @@ -367,10 +343,12 @@ public class TypeDescriptor { */ public Annotation[] getAnnotations() { if (this.field != null) { + // not caching return this.field.getAnnotations(); } else if (this.methodParameter != null) { if (this.methodParameter.getParameterIndex() < 0) { + // not caching return this.methodParameter.getMethodAnnotations(); } else { @@ -494,12 +472,20 @@ public class TypeDescriptor { // internal helpers - private Class getArrayComponentType() { - return getType().getComponentType(); + private Class resolveElementType() { + if (isArray()) { + return getType().getComponentType(); + } + else if (isCollection()) { + return resolveCollectionElementType(); + } + else { + return null; + } } - + @SuppressWarnings("unchecked") - private Class getCollectionElementType() { + private Class resolveCollectionElementType() { if (this.field != null) { return GenericCollectionTypeResolver.getCollectionFieldType(this.field); } @@ -515,12 +501,47 @@ public class TypeDescriptor { } } } - if (this.type != null) { - return GenericCollectionTypeResolver.getCollectionType((Class) this.type); + return type != null ? GenericCollectionTypeResolver.getCollectionType((Class) this.type) : null; + } + + @SuppressWarnings("unchecked") + private Class resolveMapKeyType() { + if (this.field != null) { + return GenericCollectionTypeResolver.getMapKeyFieldType(this.field); } - else { - return null; + else if (this.methodParameter != null) { + return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter); + } + else if (this.value instanceof Map) { + Map map = (Map) this.value; + if (!map.isEmpty()) { + Object key = map.keySet().iterator().next(); + if (key != null) { + return key.getClass(); + } + } + } + return type != null ? GenericCollectionTypeResolver.getMapKeyType((Class) this.type) : null; + } + + @SuppressWarnings("unchecked") + private Class resolveMapValueType() { + if (this.field != null) { + return GenericCollectionTypeResolver.getMapValueFieldType(this.field); + } + else if (this.methodParameter != null) { + return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter); + } + else if (this.value instanceof Map) { + Map map = (Map) this.value; + if (!map.isEmpty()) { + Object val = map.values().iterator().next(); + if (val != null) { + return val.getClass(); + } + } } + return type != null ? GenericCollectionTypeResolver.getMapValueType((Class) this.type) : null; } /** diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index d4b2d6709f..5ee47eec7f 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -16,19 +16,27 @@ package org.springframework.core.convert.support; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; -import static org.junit.Assert.*; +import org.junit.Ignore; import org.junit.Test; - import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; +import org.springframework.util.StopWatch; /** * @author Keith Donald @@ -193,6 +201,58 @@ public class GenericConversionServiceTests { assertEquals(input, converted); } + @Test + @Ignore + public void testPerformance1() { + GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + StopWatch watch = new StopWatch("conversionPerformance"); + watch.start("convert 4,000,000"); + for (int i = 0; i < 4000000; i++) { + conversionService.convert(3, String.class); + } + watch.stop(); + System.out.println(watch.prettyPrint()); + } + + @Test + @Ignore + public void testPerformance2() throws Exception { + GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + StopWatch watch = new StopWatch("conversionPerformance"); + watch.start("convert 4,000,000"); + List source = new LinkedList(); + source.add("1"); + source.add("2"); + source.add("3"); + TypeDescriptor td = new TypeDescriptor(getClass().getField("list")); + for (int i = 0; i < 1000000; i++) { + conversionService.convert(source, TypeDescriptor.forObject(source), td); + } + watch.stop(); + System.out.println(watch.prettyPrint()); + } + + public static List list; + + @Test + @Ignore + public void testPerformance3() throws Exception { + GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + StopWatch watch = new StopWatch("conversionPerformance"); + watch.start("convert 4,000,000"); + Map source = new HashMap(); + source.put("1", "1"); + source.put("2", "2"); + source.put("3", "3"); + TypeDescriptor td = new TypeDescriptor(getClass().getField("map")); + for (int i = 0; i < 1000000; i++) { + conversionService.convert(source, TypeDescriptor.forObject(source), td); + } + watch.stop(); + System.out.println(watch.prettyPrint()); + } + + public static Map map; private interface MyBaseInterface {