caching optmizations and performance tests
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3268 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
d8b65390ae
commit
28a652ea77
|
|
@ -73,6 +73,12 @@ public class TypeDescriptor {
|
||||||
|
|
||||||
private Object value;
|
private Object value;
|
||||||
|
|
||||||
|
private TypeDescriptor elementType;
|
||||||
|
|
||||||
|
private TypeDescriptor mapKeyType;
|
||||||
|
|
||||||
|
private TypeDescriptor mapValueType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new type descriptor from a method or constructor parameter.
|
* Create a new type descriptor from a method or constructor parameter.
|
||||||
* <p>Use this constructor when a target conversion point originates from a method parameter,
|
* <p>Use this constructor when a target conversion point originates from a method parameter,
|
||||||
|
|
@ -233,22 +239,19 @@ public class TypeDescriptor {
|
||||||
* Returns <code>null</code> if the type is neither an array or collection.
|
* Returns <code>null</code> if the type is neither an array or collection.
|
||||||
*/
|
*/
|
||||||
public Class<?> getElementType() {
|
public Class<?> getElementType() {
|
||||||
if (isArray()) {
|
return getElementTypeDescriptor().getType();
|
||||||
return getArrayComponentType();
|
|
||||||
}
|
|
||||||
else if (isCollection()) {
|
|
||||||
return getCollectionElementType();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the element type as a type descriptor.
|
* Return the element type as a type descriptor.
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getElementTypeDescriptor() {
|
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
|
* @return the element type descriptor
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getElementTypeDescriptor(Object element) {
|
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.
|
* Determine the generic key type of the wrapped Map parameter/field, if any.
|
||||||
* @return the generic type, or <code>null</code> if none
|
* @return the generic type, or <code>null</code> if none
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Class<?> getMapKeyType() {
|
public Class<?> getMapKeyType() {
|
||||||
if (isMap()) {
|
return getMapKeyTypeDescriptor().getType();
|
||||||
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<? extends Map>) this.type);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine the generic value type of the wrapped Map parameter/field, if any.
|
|
||||||
* @return the generic type, or <code>null</code> 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<? extends Map>) this.type);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns map key type as a type descriptor.
|
* Returns map key type as a type descriptor.
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getMapKeyTypeDescriptor() {
|
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
|
* @return the map key type descriptor
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getMapKeyTypeDescriptor(Object key) {
|
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 <code>null</code> if none
|
||||||
|
*/
|
||||||
|
public Class<?> getMapValueType() {
|
||||||
|
return getMapValueTypeDescriptor().getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns map value type as a type descriptor.
|
* Returns map value type as a type descriptor.
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getMapValueTypeDescriptor() {
|
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
|
* @return the map value type descriptor
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getMapValueTypeDescriptor(Object value) {
|
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() {
|
public Annotation[] getAnnotations() {
|
||||||
if (this.field != null) {
|
if (this.field != null) {
|
||||||
|
// not caching
|
||||||
return this.field.getAnnotations();
|
return this.field.getAnnotations();
|
||||||
}
|
}
|
||||||
else if (this.methodParameter != null) {
|
else if (this.methodParameter != null) {
|
||||||
if (this.methodParameter.getParameterIndex() < 0) {
|
if (this.methodParameter.getParameterIndex() < 0) {
|
||||||
|
// not caching
|
||||||
return this.methodParameter.getMethodAnnotations();
|
return this.methodParameter.getMethodAnnotations();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -494,12 +472,20 @@ public class TypeDescriptor {
|
||||||
|
|
||||||
// internal helpers
|
// internal helpers
|
||||||
|
|
||||||
private Class<?> getArrayComponentType() {
|
private Class<?> resolveElementType() {
|
||||||
return getType().getComponentType();
|
if (isArray()) {
|
||||||
|
return getType().getComponentType();
|
||||||
|
}
|
||||||
|
else if (isCollection()) {
|
||||||
|
return resolveCollectionElementType();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Class<?> getCollectionElementType() {
|
private Class<?> resolveCollectionElementType() {
|
||||||
if (this.field != null) {
|
if (this.field != null) {
|
||||||
return GenericCollectionTypeResolver.getCollectionFieldType(this.field);
|
return GenericCollectionTypeResolver.getCollectionFieldType(this.field);
|
||||||
}
|
}
|
||||||
|
|
@ -515,12 +501,47 @@ public class TypeDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.type != null) {
|
return type != null ? GenericCollectionTypeResolver.getCollectionType((Class<? extends Collection>) this.type) : null;
|
||||||
return GenericCollectionTypeResolver.getCollectionType((Class<? extends Collection>) this.type);
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Class<?> resolveMapKeyType() {
|
||||||
|
if (this.field != null) {
|
||||||
|
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field);
|
||||||
}
|
}
|
||||||
else {
|
else if (this.methodParameter != null) {
|
||||||
return 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<? extends Map>) 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<? extends Map>) this.type) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,27 @@
|
||||||
|
|
||||||
package org.springframework.core.convert.support;
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.convert.ConversionFailedException;
|
import org.springframework.core.convert.ConversionFailedException;
|
||||||
import org.springframework.core.convert.ConverterNotFoundException;
|
import org.springframework.core.convert.ConverterNotFoundException;
|
||||||
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.util.StopWatch;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
|
@ -193,6 +201,58 @@ public class GenericConversionServiceTests {
|
||||||
assertEquals(input, converted);
|
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<String> source = new LinkedList<String>();
|
||||||
|
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<Integer> list;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testPerformance3() throws Exception {
|
||||||
|
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
|
||||||
|
StopWatch watch = new StopWatch("conversionPerformance");
|
||||||
|
watch.start("convert 4,000,000");
|
||||||
|
Map<String, String> source = new HashMap<String, String>();
|
||||||
|
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<String, Integer> map;
|
||||||
|
|
||||||
private interface MyBaseInterface {
|
private interface MyBaseInterface {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue