19
19
import java .lang .annotation .Annotation ;
20
20
import java .lang .reflect .Array ;
21
21
import java .lang .reflect .Constructor ;
22
+ import java .lang .reflect .Method ;
22
23
import java .lang .reflect .Modifier ;
23
24
import java .lang .reflect .Parameter ;
24
25
import java .util .ArrayList ;
27
28
import java .util .List ;
28
29
import java .util .Map ;
29
30
import java .util .Optional ;
31
+ import java .util .function .Consumer ;
30
32
31
33
import kotlin .reflect .KFunction ;
32
34
import kotlin .reflect .KParameter ;
35
37
import org .apache .commons .logging .LogFactory ;
36
38
37
39
import org .springframework .beans .BeanUtils ;
40
+ import org .springframework .boot .context .properties .bind .Binder .Context ;
38
41
import org .springframework .boot .context .properties .source .ConfigurationPropertyName ;
39
42
import org .springframework .core .CollectionFactory ;
40
43
import org .springframework .core .DefaultParameterNameDiscoverer ;
@@ -69,7 +72,7 @@ class ValueObjectBinder implements DataObjectBinder {
69
72
@ Override
70
73
public <T > T bind (ConfigurationPropertyName name , Bindable <T > target , Binder .Context context ,
71
74
DataObjectPropertyBinder propertyBinder ) {
72
- ValueObject <T > valueObject = ValueObject .get (target , this .constructorProvider , context );
75
+ ValueObject <T > valueObject = ValueObject .get (target , this .constructorProvider , context , Discoverer . LENIENT );
73
76
if (valueObject == null ) {
74
77
return null ;
75
78
}
@@ -90,7 +93,7 @@ public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Binder.Con
90
93
91
94
@ Override
92
95
public <T > T create (Bindable <T > target , Binder .Context context ) {
93
- ValueObject <T > valueObject = ValueObject .get (target , this .constructorProvider , context );
96
+ ValueObject <T > valueObject = ValueObject .get (target , this .constructorProvider , context , Discoverer . LENIENT );
94
97
if (valueObject == null ) {
95
98
return null ;
96
99
}
@@ -102,6 +105,16 @@ public <T> T create(Bindable<T> target, Binder.Context context) {
102
105
return valueObject .instantiate (args );
103
106
}
104
107
108
+ @ Override
109
+ public <T > void onUnableToCreateInstance (Bindable <T > target , Context context , RuntimeException exception ) {
110
+ try {
111
+ ValueObject .get (target , this .constructorProvider , context , Discoverer .STRICT );
112
+ }
113
+ catch (Exception ex ) {
114
+ exception .addSuppressed (ex );
115
+ }
116
+ }
117
+
105
118
private <T > T getDefaultValue (Binder .Context context , ConstructorParameter parameter ) {
106
119
ResolvableType type = parameter .getType ();
107
120
Annotation [] annotations = parameter .getAnnotations ();
@@ -187,7 +200,7 @@ T instantiate(List<Object> args) {
187
200
188
201
@ SuppressWarnings ("unchecked" )
189
202
static <T > ValueObject <T > get (Bindable <T > bindable , BindConstructorProvider constructorProvider ,
190
- Binder .Context context ) {
203
+ Binder .Context context , ParameterNameDiscoverer parameterNameDiscoverer ) {
191
204
Class <T > type = (Class <T >) bindable .getType ().resolve ();
192
205
if (type == null || type .isEnum () || Modifier .isAbstract (type .getModifiers ())) {
193
206
return null ;
@@ -198,9 +211,10 @@ static <T> ValueObject<T> get(Bindable<T> bindable, BindConstructorProvider cons
198
211
return null ;
199
212
}
200
213
if (KotlinDetector .isKotlinType (type )) {
201
- return KotlinValueObject .get ((Constructor <T >) bindConstructor , bindable .getType ());
214
+ return KotlinValueObject .get ((Constructor <T >) bindConstructor , bindable .getType (),
215
+ parameterNameDiscoverer );
202
216
}
203
- return DefaultValueObject .get (bindConstructor , bindable .getType ());
217
+ return DefaultValueObject .get (bindConstructor , bindable .getType (), parameterNameDiscoverer );
204
218
}
205
219
206
220
}
@@ -246,12 +260,13 @@ List<ConstructorParameter> getConstructorParameters() {
246
260
return this .constructorParameters ;
247
261
}
248
262
249
- static <T > ValueObject <T > get (Constructor <T > bindConstructor , ResolvableType type ) {
263
+ static <T > ValueObject <T > get (Constructor <T > bindConstructor , ResolvableType type ,
264
+ ParameterNameDiscoverer parameterNameDiscoverer ) {
250
265
KFunction <T > kotlinConstructor = ReflectJvmMapping .getKotlinFunction (bindConstructor );
251
266
if (kotlinConstructor != null ) {
252
267
return new KotlinValueObject <>(bindConstructor , kotlinConstructor , type );
253
268
}
254
- return DefaultValueObject .get (bindConstructor , type );
269
+ return DefaultValueObject .get (bindConstructor , type , parameterNameDiscoverer );
255
270
}
256
271
257
272
}
@@ -262,8 +277,6 @@ static <T> ValueObject<T> get(Constructor<T> bindConstructor, ResolvableType typ
262
277
*/
263
278
private static final class DefaultValueObject <T > extends ValueObject <T > {
264
279
265
- private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer ();
266
-
267
280
private final List <ConstructorParameter > constructorParameters ;
268
281
269
282
private DefaultValueObject (Constructor <T > constructor , List <ConstructorParameter > constructorParameters ) {
@@ -277,12 +290,10 @@ List<ConstructorParameter> getConstructorParameters() {
277
290
}
278
291
279
292
@ SuppressWarnings ("unchecked" )
280
- static <T > ValueObject <T > get (Constructor <?> bindConstructor , ResolvableType type ) {
281
- String [] names = PARAMETER_NAME_DISCOVERER .getParameterNames (bindConstructor );
293
+ static <T > ValueObject <T > get (Constructor <?> bindConstructor , ResolvableType type ,
294
+ ParameterNameDiscoverer parameterNameDiscoverer ) {
295
+ String [] names = parameterNameDiscoverer .getParameterNames (bindConstructor );
282
296
if (names == null ) {
283
- logger .debug (LogMessage .format (
284
- "Unable to use value object binding with %s as parameter names cannot be discovered" ,
285
- bindConstructor ));
286
297
return null ;
287
298
}
288
299
List <ConstructorParameter > constructorParameters = parseConstructorParameters (bindConstructor , type , names );
@@ -339,4 +350,49 @@ ResolvableType getType() {
339
350
340
351
}
341
352
353
+ /**
354
+ * {@link ParameterNameDiscoverer} used for value data object binding.
355
+ */
356
+ static final class Discoverer implements ParameterNameDiscoverer {
357
+
358
+ private static final ParameterNameDiscoverer DEFAULT_DELEGATE = new DefaultParameterNameDiscoverer ();
359
+
360
+ private static final ParameterNameDiscoverer LENIENT = new Discoverer (DEFAULT_DELEGATE , (message ) -> {
361
+ });
362
+
363
+ private static final ParameterNameDiscoverer STRICT = new Discoverer (DEFAULT_DELEGATE , (message ) -> {
364
+ throw new IllegalStateException (message .toString ());
365
+ });
366
+
367
+ private final ParameterNameDiscoverer delegate ;
368
+
369
+ private final Consumer <LogMessage > noParameterNamesHandler ;
370
+
371
+ private Discoverer (ParameterNameDiscoverer delegate , Consumer <LogMessage > noParameterNamesHandler ) {
372
+ this .delegate = delegate ;
373
+ this .noParameterNamesHandler = noParameterNamesHandler ;
374
+ }
375
+
376
+ @ Override
377
+ public String [] getParameterNames (Method method ) {
378
+ throw new UnsupportedOperationException ();
379
+ }
380
+
381
+ @ Override
382
+ public String [] getParameterNames (Constructor <?> constructor ) {
383
+ String [] names = this .delegate .getParameterNames (constructor );
384
+ if (names != null ) {
385
+ return names ;
386
+ }
387
+ LogMessage message = LogMessage .format (
388
+ "Unable to use value object binding with constructor [%s] as parameter names cannot be discovered. "
389
+ + "Ensure that the compiler uses the '-parameters' flag" ,
390
+ constructor );
391
+ this .noParameterNamesHandler .accept (message );
392
+ logger .debug (message );
393
+ return null ;
394
+ }
395
+
396
+ }
397
+
342
398
}
0 commit comments