@@ -78,7 +78,8 @@ public static <T extends LinkBuilder> T linkTo(Object invocationValue, LinkBuild
78
78
@ Nullable BiFunction <UriComponentsBuilder , MethodInvocation , UriComponentsBuilder > additionalUriHandler ,
79
79
Function <String , UriComponentsBuilder > finisher , Supplier <ConversionService > conversionService ) {
80
80
81
- return linkTo (invocationValue , creator , additionalUriHandler ).conclude (finisher , conversionService .get ());
81
+ return linkTo (invocationValue , creator , additionalUriHandler ).conclude (finisher ,
82
+ conversionService .get ());
82
83
}
83
84
84
85
private static <T extends LinkBuilder > PreparedWebHandler <T > linkTo (Object invocationValue ,
@@ -99,6 +100,8 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invoc
99
100
100
101
return (finisher , conversionService ) -> {
101
102
103
+ FormatterFactory factory = new FormatterFactory (conversionService );
104
+
102
105
UriComponentsBuilder builder = finisher .apply (mapping );
103
106
UriTemplate template = UriTemplateFactory .templateFor (mapping == null ? "/" : mapping );
104
107
Map <String , Object > values = new HashMap <>();
@@ -114,7 +117,7 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invoc
114
117
Object source = classMappingParameters .next ();
115
118
116
119
values .put (name , variable .prepareAndEncode (
117
- HandlerMethodParameter .prepareValue (source , conversionService , TypeDescriptor .forObject (source ))));
120
+ HandlerMethodParameter .prepareValue (source , factory , TypeDescriptor .forObject (source ))));
118
121
}
119
122
120
123
Method method = invocation .getMethod ();
@@ -126,7 +129,7 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invoc
126
129
TemplateVariable variable = TemplateVariable .segment (parameter .getVariableName ());
127
130
Object verifiedValue = parameter .getVerifiedValue (arguments );
128
131
Object preparedValue = verifiedValue == null ? verifiedValue
129
- : parameter .prepareValue (verifiedValue , conversionService );
132
+ : parameter .prepareValue (verifiedValue , factory );
130
133
131
134
values .put (variable .getName (), variable .prepareAndEncode (preparedValue ));
132
135
}
@@ -135,7 +138,7 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invoc
135
138
136
139
for (HandlerMethodParameter parameter : parameters .getParameterAnnotatedWith (RequestParam .class , arguments )) {
137
140
138
- bindRequestParameters (builder , parameter , arguments , conversionService );
141
+ bindRequestParameters (builder , parameter , arguments , factory );
139
142
140
143
boolean isSkipValue = SKIP_VALUE .equals (parameter .getVerifiedValue (arguments ));
141
144
boolean isMapParameter = Map .class .isAssignableFrom (parameter .parameter .getParameterType ());
@@ -186,7 +189,7 @@ private static <T extends LinkBuilder> PreparedWebHandler<T> linkTo(Object invoc
186
189
*/
187
190
@ SuppressWarnings ("unchecked" )
188
191
private static void bindRequestParameters (UriComponentsBuilder builder , HandlerMethodParameter parameter ,
189
- Object [] arguments , ConversionService conversionService ) {
192
+ Object [] arguments , FormatterFactory factory ) {
190
193
191
194
Object value = parameter .getVerifiedValue (arguments );
192
195
@@ -198,7 +201,7 @@ private static void bindRequestParameters(UriComponentsBuilder builder, HandlerM
198
201
199
202
if (value instanceof MultiValueMap ) {
200
203
201
- Map <String , List <?>> requestParams = (Map <String , List <?>>) parameter .prepareValue (value , conversionService );
204
+ Map <String , List <?>> requestParams = (Map <String , List <?>>) parameter .prepareValue (value , factory );
202
205
203
206
for (Entry <String , List <?>> entry : requestParams .entrySet ()) {
204
207
for (Object element : entry .getValue ()) {
@@ -212,7 +215,7 @@ private static void bindRequestParameters(UriComponentsBuilder builder, HandlerM
212
215
213
216
if (value instanceof Map ) {
214
217
215
- Map <String , ?> requestParams = (Map <String , ?>) parameter .prepareValue (value , conversionService );
218
+ Map <String , ?> requestParams = (Map <String , ?>) parameter .prepareValue (value , factory );
216
219
217
220
for (Entry <String , ?> entry : requestParams .entrySet ()) {
218
221
@@ -234,7 +237,7 @@ private static void bindRequestParameters(UriComponentsBuilder builder, HandlerM
234
237
235
238
if (value instanceof Collection ) {
236
239
237
- Collection <?> collection = (Collection <?>) parameter .prepareValue (value , conversionService );
240
+ Collection <?> collection = (Collection <?>) parameter .prepareValue (value , factory );
238
241
239
242
if (parameter .isNonComposite ()) {
240
243
builder .queryParam (key , variable .prepareAndEncode (collection ));
@@ -256,31 +259,11 @@ private static void bindRequestParameters(UriComponentsBuilder builder, HandlerM
256
259
257
260
} else {
258
261
if (key != null ) {
259
- builder .queryParam (key , variable .prepareAndEncode (parameter .prepareValue (value , conversionService )));
262
+ builder .queryParam (key , variable .prepareAndEncode (parameter .prepareValue (value , factory )));
260
263
}
261
264
}
262
265
}
263
266
264
- private static Function <Object , String > getFormatter (ConversionService conversionService , TypeDescriptor descriptor ) {
265
-
266
- return source -> {
267
-
268
- if (String .class .isInstance (source )) {
269
- return (String ) source ;
270
- }
271
-
272
- Object result = conversionService .canConvert (descriptor , STRING_DESCRIPTOR )
273
- ? conversionService .convert (source , descriptor , STRING_DESCRIPTOR )
274
- : source == null ? null : source .toString ();
275
-
276
- if (result == null ) {
277
- throw new IllegalArgumentException (String .format ("Conversion of value %s resulted in null!" , source ));
278
- }
279
-
280
- return (String ) result ;
281
- };
282
- }
283
-
284
267
private static class HandlerMethodParameters {
285
268
286
269
private static final List <Class <? extends Annotation >> ANNOTATIONS = Arrays .asList (RequestParam .class ,
@@ -419,7 +402,7 @@ public String getVariableName() {
419
402
return variableName ;
420
403
}
421
404
422
- public Object prepareValue (Object value , ConversionService conversionService ) {
405
+ public Object prepareValue (Object value , FormatterFactory conversionService ) {
423
406
424
407
Object result = prepareValue (value , conversionService , typeDescriptor );
425
408
@@ -428,7 +411,7 @@ public Object prepareValue(Object value, ConversionService conversionService) {
428
411
429
412
@ Nullable
430
413
@ SuppressWarnings ("unchecked" )
431
- public static Object prepareValue (@ Nullable Object value , ConversionService conversionService ,
414
+ public static Object prepareValue (@ Nullable Object value , FormatterFactory factory ,
432
415
@ Nullable TypeDescriptor descriptor ) {
433
416
434
417
if (descriptor == null || value == null ) {
@@ -437,14 +420,18 @@ public static Object prepareValue(@Nullable Object value, ConversionService conv
437
420
438
421
value = ObjectUtils .unwrapOptional (value );
439
422
423
+ if (String .class .isInstance (value )) {
424
+ return value ;
425
+ }
426
+
440
427
if (Collection .class .isInstance (value )) {
441
428
442
429
List <Object > prepared = new ArrayList <>();
443
430
444
431
for (Object element : (Collection <?>) value ) {
445
432
446
433
TypeDescriptor elementTypeDescriptor = descriptor .elementTypeDescriptor (element );
447
- prepared .add (prepareValue (element , conversionService , elementTypeDescriptor ));
434
+ prepared .add (prepareValue (element , factory , elementTypeDescriptor ));
448
435
}
449
436
450
437
return prepared ;
@@ -459,14 +446,14 @@ public static Object prepareValue(@Nullable Object value, ConversionService conv
459
446
TypeDescriptor keyTypeDescriptor = descriptor .getMapKeyTypeDescriptor (entry .getKey ());
460
447
TypeDescriptor elementTypeDescriptor = descriptor .elementTypeDescriptor (entry .getValue ());
461
448
462
- prepared .put (prepareValue (entry .getKey (), conversionService , keyTypeDescriptor ),
463
- prepareValue (entry .getValue (), conversionService , elementTypeDescriptor ));
449
+ prepared .put (prepareValue (entry .getKey (), factory , keyTypeDescriptor ),
450
+ prepareValue (entry .getValue (), factory , elementTypeDescriptor ));
464
451
}
465
452
466
453
return prepared ;
467
454
}
468
455
469
- return getFormatter (conversionService , descriptor ).apply (value );
456
+ return factory . getFormatter (descriptor ).apply (value );
470
457
}
471
458
472
459
private String determineVariableName () {
@@ -508,6 +495,60 @@ public Object getVerifiedValue(Object[] values) {
508
495
public abstract boolean isRequired ();
509
496
}
510
497
498
+ /**
499
+ * Factory to create to-{@link String} converters by type. Caching, to avoid repeated calculations and
500
+ * {@link Function} object creation.
501
+ *
502
+ * @author Oliver Drotbohm
503
+ */
504
+ private static class FormatterFactory {
505
+
506
+ private static final Function <Object , String > DEFAULT = source -> source == null ? null : source .toString ();
507
+
508
+ private final Map <TypeDescriptor , Function <Object , String >> formatters = new HashMap <>();
509
+ private final ConversionService conversionService ;
510
+
511
+ /**
512
+ * Creates a new {@link FormatterFactory} for the given {@link ConversionService}.
513
+ *
514
+ * @param conversionService must not be {@literal null}.
515
+ */
516
+ public FormatterFactory (ConversionService conversionService ) {
517
+ this .conversionService = conversionService ;
518
+ }
519
+
520
+ /**
521
+ * Return the formatting function to map objects of the given {@link TypeDescriptor} to String.
522
+ *
523
+ * @param descriptor must not be {@literal null}.
524
+ * @return will never be {@literal null}.
525
+ */
526
+ public Function <Object , String > getFormatter (TypeDescriptor descriptor ) {
527
+
528
+ if (STRING_DESCRIPTOR .equals (descriptor )) {
529
+ return DEFAULT ;
530
+ }
531
+
532
+ return formatters .computeIfAbsent (descriptor , it -> {
533
+
534
+ if (!conversionService .canConvert (descriptor , STRING_DESCRIPTOR )) {
535
+ return DEFAULT ;
536
+ }
537
+
538
+ return source -> {
539
+
540
+ Object result = conversionService .convert (source , descriptor , STRING_DESCRIPTOR );
541
+
542
+ if (result == null ) {
543
+ throw new IllegalArgumentException (String .format ("Conversion of value %s resulted in null!" , source ));
544
+ }
545
+
546
+ return (String ) result ;
547
+ };
548
+ });
549
+ }
550
+ }
551
+
511
552
/**
512
553
* {@link HandlerMethodParameter} implementation to work with {@link RequestParam}.
513
554
*
0 commit comments