25
25
import java .util .List ;
26
26
import java .util .Map ;
27
27
import java .util .Set ;
28
+ import java .util .concurrent .ConcurrentHashMap ;
28
29
29
30
import org .apache .commons .logging .Log ;
30
31
import org .apache .commons .logging .LogFactory ;
@@ -65,11 +66,23 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t
65
66
}
66
67
};
67
68
69
+ private static final GenericConverter NO_MATCH = new GenericConverter () {
70
+ public Set <ConvertiblePair > getConvertibleTypes () {
71
+ throw new UnsupportedOperationException ();
72
+ }
73
+ public Object convert (Object source , TypeDescriptor sourceType , TypeDescriptor targetType ) {
74
+ throw new UnsupportedOperationException ();
75
+ }
76
+ public String toString () {
77
+ return "null" ;
78
+ }
79
+ };
68
80
69
81
private final Map <Class <?>, Map <Class <?>, MatchableConverters >> converters =
70
82
new HashMap <Class <?>, Map <Class <?>, MatchableConverters >>(36 );
71
83
72
-
84
+ private final Map <ConverterCacheKey , GenericConverter > converterCache = new ConcurrentHashMap <ConverterCacheKey , GenericConverter >();
85
+
73
86
// implementing ConverterRegistry
74
87
75
88
public void addConverter (GenericConverter converter ) {
@@ -219,20 +232,45 @@ protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor tar
219
232
220
233
/**
221
234
* Hook method to lookup the converter for a given sourceType/targetType pair.
222
- * First queries this ConversionService's converter map.
223
- * <p>Returns <code>null</code> if this ConversionService simply cannot convert
224
- * between sourceType and targetType. Subclasses may override.
235
+ * First queries this ConversionService's converter cache.
236
+ * On a cache miss, then performs an exhaustive search for a matching converter.
237
+ * If no converter matches, returns the default converter.
238
+ * Subclasses may override.
225
239
* @param sourceType the source type to convert from
226
240
* @param targetType the target type to convert to
227
241
* @return the generic converter that will perform the conversion, or <code>null</code> if no suitable converter was found
242
+ * @see #getDefaultConverter(TypeDescriptor, TypeDescriptor)
228
243
*/
229
244
protected GenericConverter getConverter (TypeDescriptor sourceType , TypeDescriptor targetType ) {
230
- GenericConverter converter = findConverterForClassPair (sourceType , targetType );
245
+ ConverterCacheKey key = new ConverterCacheKey (sourceType , targetType );
246
+ GenericConverter converter = converterCache .get (key );
231
247
if (converter != null ) {
232
- return converter ;
233
- }
234
- else {
235
- return getDefaultConverter (sourceType , targetType );
248
+ if (logger .isDebugEnabled ()) {
249
+ logger .debug ("Matched cached converter " + converter );
250
+ }
251
+ return converter != NO_MATCH ? converter : null ;
252
+ } else {
253
+ converter = findConverterForClassPair (sourceType , targetType );
254
+ if (converter != null ) {
255
+ if (logger .isTraceEnabled ()) {
256
+ logger .trace ("Caching under " + key );
257
+ }
258
+ converterCache .put (key , converter );
259
+ return converter ;
260
+ }
261
+ converter = getDefaultConverter (sourceType , targetType );
262
+ if (converter != null ) {
263
+ if (logger .isTraceEnabled ()) {
264
+ logger .trace ("Caching under " + key );
265
+ }
266
+ converterCache .put (key , converter );
267
+ return converter ;
268
+ }
269
+ if (logger .isTraceEnabled ()) {
270
+ logger .trace ("Caching NO_MATCH under " + key );
271
+ }
272
+ converterCache .put (key , NO_MATCH );
273
+ return null ;
236
274
}
237
275
}
238
276
@@ -297,7 +335,7 @@ private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, Ty
297
335
while (!classQueue .isEmpty ()) {
298
336
Class <?> currentClass = classQueue .removeLast ();
299
337
if (logger .isTraceEnabled ()) {
300
- logger .trace ("Looking for converters indexed by sourceType [" + currentClass .getName () + "]" );
338
+ logger .trace ("Searching for converters indexed by sourceType [" + currentClass .getName () + "]" );
301
339
}
302
340
Map <Class <?>, MatchableConverters > converters = getTargetConvertersForSource (currentClass );
303
341
GenericConverter converter = getMatchingConverterForTarget (sourceType , targetType , converters );
@@ -318,7 +356,7 @@ private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, Ty
318
356
while (!classQueue .isEmpty ()) {
319
357
Class <?> currentClass = classQueue .removeLast ();
320
358
if (logger .isTraceEnabled ()) {
321
- logger .trace ("Looking for converters indexed by sourceType [" + currentClass .getName () + "]" );
359
+ logger .trace ("Searching for converters indexed by sourceType [" + currentClass .getName () + "]" );
322
360
}
323
361
Map <Class <?>, MatchableConverters > converters = getTargetConvertersForSource (currentClass );
324
362
GenericConverter converter = getMatchingConverterForTarget (sourceType , targetType , converters );
@@ -552,5 +590,33 @@ public String toString() {
552
590
}
553
591
}
554
592
}
593
+
594
+ private static class ConverterCacheKey {
595
+
596
+ private TypeDescriptor sourceType ;
597
+
598
+ private TypeDescriptor targetType ;
599
+
600
+ public ConverterCacheKey (TypeDescriptor sourceType , TypeDescriptor targetType ) {
601
+ this .sourceType = sourceType ;
602
+ this .targetType = targetType ;
603
+ }
604
+
605
+ public boolean equals (Object o ) {
606
+ if (!(o instanceof ConverterCacheKey )) {
607
+ return false ;
608
+ }
609
+ ConverterCacheKey key = (ConverterCacheKey ) o ;
610
+ return sourceType .equals (key .sourceType ) && targetType .equals (key .targetType );
611
+ }
612
+
613
+ public int hashCode () {
614
+ return sourceType .hashCode () + targetType .hashCode ();
615
+ }
616
+
617
+ public String toString () {
618
+ return "[ConverterCacheKey sourceType = " + sourceType + ", targetType = " + targetType + "]" ;
619
+ }
620
+ }
555
621
556
622
}
0 commit comments