Skip to content

Commit e603892

Browse files
author
Keith Donald
committed
caching optmizations and performance tests
1 parent 066b4d5 commit e603892

File tree

2 files changed

+155
-74
lines changed

2 files changed

+155
-74
lines changed

org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

Lines changed: 93 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ public class TypeDescriptor {
7373

7474
private Object value;
7575

76+
private TypeDescriptor elementType;
77+
78+
private TypeDescriptor mapKeyType;
79+
80+
private TypeDescriptor mapValueType;
81+
7682
/**
7783
* Create a new type descriptor from a method or constructor parameter.
7884
* <p>Use this constructor when a target conversion point originates from a method parameter,
@@ -233,22 +239,19 @@ public boolean isCollection() {
233239
* Returns <code>null</code> if the type is neither an array or collection.
234240
*/
235241
public Class<?> getElementType() {
236-
if (isArray()) {
237-
return getArrayComponentType();
238-
}
239-
else if (isCollection()) {
240-
return getCollectionElementType();
241-
}
242-
else {
243-
return null;
244-
}
242+
return getElementTypeDescriptor().getType();
245243
}
246244

247245
/**
248246
* Return the element type as a type descriptor.
249247
*/
250248
public TypeDescriptor getElementTypeDescriptor() {
251-
return forElementType(getElementType());
249+
if (elementType != null) {
250+
return elementType;
251+
} else {
252+
elementType = forElementType(resolveElementType());
253+
return elementType;
254+
}
252255
}
253256

254257
/**
@@ -257,7 +260,8 @@ public TypeDescriptor getElementTypeDescriptor() {
257260
* @return the element type descriptor
258261
*/
259262
public TypeDescriptor getElementTypeDescriptor(Object element) {
260-
return getElementType() != null ? getElementTypeDescriptor() : TypeDescriptor.forObject(element);
263+
TypeDescriptor elementType = getElementTypeDescriptor();
264+
return elementType != TypeDescriptor.NULL ? elementType : TypeDescriptor.forObject(element);
261265
}
262266

263267
/**
@@ -278,63 +282,20 @@ public boolean isMapEntryTypeKnown() {
278282
* Determine the generic key type of the wrapped Map parameter/field, if any.
279283
* @return the generic type, or <code>null</code> if none
280284
*/
281-
@SuppressWarnings("unchecked")
282285
public Class<?> getMapKeyType() {
283-
if (isMap()) {
284-
if (this.field != null) {
285-
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field);
286-
}
287-
else if (this.methodParameter != null) {
288-
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
289-
}
290-
else if (this.value instanceof Map) {
291-
Map map = (Map) this.value;
292-
if (!map.isEmpty()) {
293-
Object key = map.keySet().iterator().next();
294-
if (key != null) {
295-
return key.getClass();
296-
}
297-
}
298-
}
299-
return GenericCollectionTypeResolver.getMapKeyType((Class<? extends Map>) this.type);
300-
} else {
301-
return null;
302-
}
303-
}
304-
305-
/**
306-
* Determine the generic value type of the wrapped Map parameter/field, if any.
307-
* @return the generic type, or <code>null</code> if none
308-
*/
309-
@SuppressWarnings("unchecked")
310-
public Class<?> getMapValueType() {
311-
if (isMap()) {
312-
if (this.field != null) {
313-
return GenericCollectionTypeResolver.getMapValueFieldType(this.field);
314-
}
315-
else if (this.methodParameter != null) {
316-
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
317-
}
318-
else if (this.value instanceof Map) {
319-
Map map = (Map) this.value;
320-
if (!map.isEmpty()) {
321-
Object val = map.values().iterator().next();
322-
if (val != null) {
323-
return val.getClass();
324-
}
325-
}
326-
}
327-
return GenericCollectionTypeResolver.getMapValueType((Class<? extends Map>) this.type);
328-
} else {
329-
return null;
330-
}
286+
return getMapKeyTypeDescriptor().getType();
331287
}
332288

333289
/**
334290
* Returns map key type as a type descriptor.
335291
*/
336292
public TypeDescriptor getMapKeyTypeDescriptor() {
337-
return forElementType(getMapKeyType());
293+
if (mapKeyType != null) {
294+
return mapKeyType;
295+
} else {
296+
mapKeyType = isMap() ? forElementType(resolveMapKeyType()) : null;
297+
return mapKeyType;
298+
}
338299
}
339300

340301
/**
@@ -343,14 +304,28 @@ public TypeDescriptor getMapKeyTypeDescriptor() {
343304
* @return the map key type descriptor
344305
*/
345306
public TypeDescriptor getMapKeyTypeDescriptor(Object key) {
346-
return getMapKeyType() != null ? getMapKeyTypeDescriptor() : TypeDescriptor.forObject(key);
307+
TypeDescriptor keyType = getMapKeyTypeDescriptor();
308+
return keyType != TypeDescriptor.NULL ? keyType : TypeDescriptor.forObject(key);
309+
}
310+
311+
/**
312+
* Determine the generic value type of the wrapped Map parameter/field, if any.
313+
* @return the generic type, or <code>null</code> if none
314+
*/
315+
public Class<?> getMapValueType() {
316+
return getMapValueTypeDescriptor().getType();
347317
}
348318

349319
/**
350320
* Returns map value type as a type descriptor.
351321
*/
352322
public TypeDescriptor getMapValueTypeDescriptor() {
353-
return forElementType(getMapValueType());
323+
if (mapValueType != null) {
324+
return mapValueType;
325+
} else {
326+
mapValueType = isMap() ? forElementType(resolveMapValueType()) : null;
327+
return mapValueType;
328+
}
354329
}
355330

356331
/**
@@ -359,18 +334,21 @@ public TypeDescriptor getMapValueTypeDescriptor() {
359334
* @return the map value type descriptor
360335
*/
361336
public TypeDescriptor getMapValueTypeDescriptor(Object value) {
362-
return getMapValueType() != null ? getMapValueTypeDescriptor() : TypeDescriptor.forObject(value);
337+
TypeDescriptor valueType = getMapValueTypeDescriptor();
338+
return valueType != TypeDescriptor.NULL ? valueType : TypeDescriptor.forObject(value);
363339
}
364340

365341
/**
366342
* Obtain the annotations associated with the wrapped parameter/field, if any.
367343
*/
368344
public Annotation[] getAnnotations() {
369345
if (this.field != null) {
346+
// not caching
370347
return this.field.getAnnotations();
371348
}
372349
else if (this.methodParameter != null) {
373350
if (this.methodParameter.getParameterIndex() < 0) {
351+
// not caching
374352
return this.methodParameter.getMethodAnnotations();
375353
}
376354
else {
@@ -494,12 +472,20 @@ public String toString() {
494472

495473
// internal helpers
496474

497-
private Class<?> getArrayComponentType() {
498-
return getType().getComponentType();
475+
private Class<?> resolveElementType() {
476+
if (isArray()) {
477+
return getType().getComponentType();
478+
}
479+
else if (isCollection()) {
480+
return resolveCollectionElementType();
481+
}
482+
else {
483+
return null;
484+
}
499485
}
500-
486+
501487
@SuppressWarnings("unchecked")
502-
private Class<?> getCollectionElementType() {
488+
private Class<?> resolveCollectionElementType() {
503489
if (this.field != null) {
504490
return GenericCollectionTypeResolver.getCollectionFieldType(this.field);
505491
}
@@ -515,12 +501,47 @@ else if (this.value instanceof Collection) {
515501
}
516502
}
517503
}
518-
if (this.type != null) {
519-
return GenericCollectionTypeResolver.getCollectionType((Class<? extends Collection>) this.type);
504+
return type != null ? GenericCollectionTypeResolver.getCollectionType((Class<? extends Collection>) this.type) : null;
505+
}
506+
507+
@SuppressWarnings("unchecked")
508+
private Class<?> resolveMapKeyType() {
509+
if (this.field != null) {
510+
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field);
520511
}
521-
else {
522-
return null;
512+
else if (this.methodParameter != null) {
513+
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
514+
}
515+
else if (this.value instanceof Map<?, ?>) {
516+
Map<?, ?> map = (Map<?, ?>) this.value;
517+
if (!map.isEmpty()) {
518+
Object key = map.keySet().iterator().next();
519+
if (key != null) {
520+
return key.getClass();
521+
}
522+
}
523+
}
524+
return type != null ? GenericCollectionTypeResolver.getMapKeyType((Class<? extends Map>) this.type) : null;
525+
}
526+
527+
@SuppressWarnings("unchecked")
528+
private Class<?> resolveMapValueType() {
529+
if (this.field != null) {
530+
return GenericCollectionTypeResolver.getMapValueFieldType(this.field);
531+
}
532+
else if (this.methodParameter != null) {
533+
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
534+
}
535+
else if (this.value instanceof Map<?, ?>) {
536+
Map<?, ?> map = (Map<?, ?>) this.value;
537+
if (!map.isEmpty()) {
538+
Object val = map.values().iterator().next();
539+
if (val != null) {
540+
return val.getClass();
541+
}
542+
}
523543
}
544+
return type != null ? GenericCollectionTypeResolver.getMapValueType((Class<? extends Map>) this.type) : null;
524545
}
525546

526547
/**

org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,27 @@
1616

1717
package org.springframework.core.convert.support;
1818

19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertNull;
22+
import static org.junit.Assert.assertSame;
23+
import static org.junit.Assert.assertTrue;
24+
import static org.junit.Assert.fail;
25+
1926
import java.util.ArrayList;
2027
import java.util.HashMap;
2128
import java.util.LinkedHashMap;
29+
import java.util.LinkedList;
2230
import java.util.List;
2331
import java.util.Map;
2432

25-
import static org.junit.Assert.*;
33+
import org.junit.Ignore;
2634
import org.junit.Test;
27-
2835
import org.springframework.core.convert.ConversionFailedException;
2936
import org.springframework.core.convert.ConverterNotFoundException;
3037
import org.springframework.core.convert.TypeDescriptor;
3138
import org.springframework.core.convert.converter.Converter;
39+
import org.springframework.util.StopWatch;
3240

3341
/**
3442
* @author Keith Donald
@@ -193,6 +201,58 @@ public void testWildcardMap() throws Exception {
193201
assertEquals(input, converted);
194202
}
195203

204+
@Test
205+
@Ignore
206+
public void testPerformance1() {
207+
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
208+
StopWatch watch = new StopWatch("conversionPerformance");
209+
watch.start("convert 4,000,000");
210+
for (int i = 0; i < 4000000; i++) {
211+
conversionService.convert(3, String.class);
212+
}
213+
watch.stop();
214+
System.out.println(watch.prettyPrint());
215+
}
216+
217+
@Test
218+
@Ignore
219+
public void testPerformance2() throws Exception {
220+
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
221+
StopWatch watch = new StopWatch("conversionPerformance");
222+
watch.start("convert 4,000,000");
223+
List<String> source = new LinkedList<String>();
224+
source.add("1");
225+
source.add("2");
226+
source.add("3");
227+
TypeDescriptor td = new TypeDescriptor(getClass().getField("list"));
228+
for (int i = 0; i < 1000000; i++) {
229+
conversionService.convert(source, TypeDescriptor.forObject(source), td);
230+
}
231+
watch.stop();
232+
System.out.println(watch.prettyPrint());
233+
}
234+
235+
public static List<Integer> list;
236+
237+
@Test
238+
@Ignore
239+
public void testPerformance3() throws Exception {
240+
GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
241+
StopWatch watch = new StopWatch("conversionPerformance");
242+
watch.start("convert 4,000,000");
243+
Map<String, String> source = new HashMap<String, String>();
244+
source.put("1", "1");
245+
source.put("2", "2");
246+
source.put("3", "3");
247+
TypeDescriptor td = new TypeDescriptor(getClass().getField("map"));
248+
for (int i = 0; i < 1000000; i++) {
249+
conversionService.convert(source, TypeDescriptor.forObject(source), td);
250+
}
251+
watch.stop();
252+
System.out.println(watch.prettyPrint());
253+
}
254+
255+
public static Map<String, Integer> map;
196256

197257
private interface MyBaseInterface {
198258

0 commit comments

Comments
 (0)