|
17 | 17 | package org.springframework.boot.context.properties.bind;
|
18 | 18 |
|
19 | 19 | import java.util.ArrayDeque;
|
20 |
| -import java.util.ArrayList; |
21 | 20 | import java.util.Arrays;
|
22 | 21 | import java.util.Collection;
|
23 | 22 | import java.util.Collections;
|
24 | 23 | import java.util.Deque;
|
25 | 24 | import java.util.HashSet;
|
26 | 25 | import java.util.List;
|
27 | 26 | import java.util.Map;
|
28 |
| -import java.util.Objects; |
29 | 27 | import java.util.Set;
|
30 | 28 | import java.util.function.Consumer;
|
31 | 29 | import java.util.function.Supplier;
|
32 |
| -import java.util.stream.Stream; |
33 | 30 |
|
34 | 31 | import org.springframework.beans.PropertyEditorRegistry;
|
35 | 32 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
@@ -58,14 +55,7 @@ public class Binder {
|
58 | 55 | private static final Set<Class<?>> NON_BEAN_CLASSES = Collections
|
59 | 56 | .unmodifiableSet(new HashSet<>(Arrays.asList(Object.class, Class.class)));
|
60 | 57 |
|
61 |
| - private static final List<BeanBinder> BEAN_BINDERS; |
62 |
| - |
63 |
| - static { |
64 |
| - List<BeanBinder> binders = new ArrayList<>(); |
65 |
| - binders.add(new ConstructorParametersBinder()); |
66 |
| - binders.add(new JavaBeanBinder()); |
67 |
| - BEAN_BINDERS = Collections.unmodifiableList(binders); |
68 |
| - } |
| 58 | + private static final BeanBinder[] BEAN_BINDERS = { new ConstructorParametersBinder(), new JavaBeanBinder() }; |
69 | 59 |
|
70 | 60 | private final Iterable<ConfigurationPropertySource> sources;
|
71 | 61 |
|
@@ -196,40 +186,122 @@ public <T> BindResult<T> bind(String name, Bindable<T> target, BindHandler handl
|
196 | 186 | * @return the binding result (never {@code null})
|
197 | 187 | */
|
198 | 188 | public <T> BindResult<T> bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler) {
|
| 189 | + T bound = bind(name, target, handler, false); |
| 190 | + return BindResult.of(bound); |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Bind the specified target {@link Class} using this binder's |
| 195 | + * {@link ConfigurationPropertySource property sources} or create a new instance using |
| 196 | + * the type of the {@link Bindable} if the result of the binding is {@code null}. |
| 197 | + * @param name the configuration property name to bind |
| 198 | + * @param target the target class |
| 199 | + * @param <T> the bound type |
| 200 | + * @return the bound or created object |
| 201 | + * @since 2.2.0 |
| 202 | + * @see #bind(ConfigurationPropertyName, Bindable, BindHandler) |
| 203 | + */ |
| 204 | + public <T> T bindOrCreate(String name, Class<T> target) { |
| 205 | + return bindOrCreate(name, Bindable.of(target)); |
| 206 | + } |
| 207 | + |
| 208 | + /** |
| 209 | + * Bind the specified target {@link Bindable} using this binder's |
| 210 | + * {@link ConfigurationPropertySource property sources} or create a new instance using |
| 211 | + * the type of the {@link Bindable} if the result of the binding is {@code null}. |
| 212 | + * @param name the configuration property name to bind |
| 213 | + * @param target the target bindable |
| 214 | + * @param <T> the bound type |
| 215 | + * @return the bound or created object |
| 216 | + * @since 2.2.0 |
| 217 | + * @see #bindOrCreate(ConfigurationPropertyName, Bindable, BindHandler) |
| 218 | + */ |
| 219 | + public <T> T bindOrCreate(String name, Bindable<T> target) { |
| 220 | + return bindOrCreate(ConfigurationPropertyName.of(name), target, null); |
| 221 | + } |
| 222 | + |
| 223 | + /** |
| 224 | + * Bind the specified target {@link Bindable} using this binder's |
| 225 | + * {@link ConfigurationPropertySource property sources} or create a new instance using |
| 226 | + * the type of the {@link Bindable} if the result of the binding is {@code null}. |
| 227 | + * @param name the configuration property name to bind |
| 228 | + * @param target the target bindable |
| 229 | + * @param handler the bind handler |
| 230 | + * @param <T> the bound type |
| 231 | + * @return the bound or created object |
| 232 | + * @since 2.2.0 |
| 233 | + * @see #bindOrCreate(ConfigurationPropertyName, Bindable, BindHandler) |
| 234 | + */ |
| 235 | + public <T> T bindOrCreate(String name, Bindable<T> target, BindHandler handler) { |
| 236 | + return bindOrCreate(ConfigurationPropertyName.of(name), target, handler); |
| 237 | + } |
| 238 | + |
| 239 | + /** |
| 240 | + * Bind the specified target {@link Bindable} using this binder's |
| 241 | + * {@link ConfigurationPropertySource property sources} or create a new instance using |
| 242 | + * the type of the {@link Bindable} if the result of the binding is {@code null}. |
| 243 | + * @param name the configuration property name to bind |
| 244 | + * @param target the target bindable |
| 245 | + * @param handler the bind handler (may be {@code null}) |
| 246 | + * @param <T> the bound or created type |
| 247 | + * @since 2.2.0 |
| 248 | + * @return the bound or created object |
| 249 | + */ |
| 250 | + public <T> T bindOrCreate(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler) { |
| 251 | + return bind(name, target, handler, true); |
| 252 | + } |
| 253 | + |
| 254 | + private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, boolean create) { |
199 | 255 | Assert.notNull(name, "Name must not be null");
|
200 | 256 | Assert.notNull(target, "Target must not be null");
|
201 | 257 | handler = (handler != null) ? handler : BindHandler.DEFAULT;
|
202 | 258 | Context context = new Context();
|
203 |
| - T bound = bind(name, target, handler, context, false); |
204 |
| - return BindResult.of(bound); |
| 259 | + return bind(name, target, handler, context, false, create); |
205 | 260 | }
|
206 | 261 |
|
207 |
| - protected final <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context, |
208 |
| - boolean allowRecursiveBinding) { |
| 262 | + private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context, |
| 263 | + boolean allowRecursiveBinding, boolean create) { |
209 | 264 | context.clearConfigurationProperty();
|
210 | 265 | try {
|
211 | 266 | target = handler.onStart(name, target, context);
|
212 | 267 | if (target == null) {
|
213 |
| - return null; |
| 268 | + return handleBindResult(name, target, handler, context, null, create); |
214 | 269 | }
|
215 | 270 | Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
|
216 |
| - return handleBindResult(name, target, handler, context, bound); |
| 271 | + return handleBindResult(name, target, handler, context, bound, create); |
217 | 272 | }
|
218 | 273 | catch (Exception ex) {
|
219 | 274 | return handleBindError(name, target, handler, context, ex);
|
220 | 275 | }
|
221 | 276 | }
|
222 | 277 |
|
223 | 278 | private <T> T handleBindResult(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
|
224 |
| - Context context, Object result) throws Exception { |
| 279 | + Context context, Object result, boolean create) throws Exception { |
225 | 280 | if (result != null) {
|
226 | 281 | result = handler.onSuccess(name, target, context, result);
|
227 | 282 | result = context.getConverter().convert(result, target);
|
228 | 283 | }
|
| 284 | + if (result == null && create) { |
| 285 | + result = createBean(target, context); |
| 286 | + result = handler.onCreate(name, target, context, result); |
| 287 | + result = context.getConverter().convert(result, target); |
| 288 | + Assert.state(result != null, () -> "Unable to create instance for " + target.getType()); |
| 289 | + } |
229 | 290 | handler.onFinish(name, target, context, result);
|
230 | 291 | return context.getConverter().convert(result, target);
|
231 | 292 | }
|
232 | 293 |
|
| 294 | + private Object createBean(Bindable<?> target, Context context) { |
| 295 | + Class<?> type = target.getType().resolve(); |
| 296 | + for (BeanBinder beanBinder : BEAN_BINDERS) { |
| 297 | + Object bean = beanBinder.create(type, context); |
| 298 | + if (bean != null) { |
| 299 | + return bean; |
| 300 | + } |
| 301 | + } |
| 302 | + return null; |
| 303 | + } |
| 304 | + |
233 | 305 | private <T> T handleBindError(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
|
234 | 306 | Context context, Exception error) {
|
235 | 307 | try {
|
@@ -288,7 +360,7 @@ private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> tar
|
288 | 360 | Context context, AggregateBinder<?> aggregateBinder) {
|
289 | 361 | AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
|
290 | 362 | boolean allowRecursiveBinding = aggregateBinder.isAllowRecursiveBinding(source);
|
291 |
| - Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding); |
| 363 | + Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding, false); |
292 | 364 | return context.withSource(source, supplier);
|
293 | 365 | };
|
294 | 366 | return context.withIncreasedDepth(() -> aggregateBinder.bind(name, target, elementBinder));
|
@@ -325,10 +397,15 @@ private Object bindBean(ConfigurationPropertyName name, Bindable<?> target, Bind
|
325 | 397 | return null;
|
326 | 398 | }
|
327 | 399 | BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
|
328 |
| - propertyTarget, handler, context, false); |
| 400 | + propertyTarget, handler, context, false, false); |
329 | 401 | return context.withBean(type, () -> {
|
330 |
| - Stream<?> boundBeans = BEAN_BINDERS.stream().map((b) -> b.bind(name, target, context, propertyBinder)); |
331 |
| - return boundBeans.filter(Objects::nonNull).findFirst().orElse(null); |
| 402 | + for (BeanBinder beanBinder : BEAN_BINDERS) { |
| 403 | + Object bean = beanBinder.bind(name, target, context, propertyBinder); |
| 404 | + if (bean != null) { |
| 405 | + return bean; |
| 406 | + } |
| 407 | + } |
| 408 | + return null; |
332 | 409 | });
|
333 | 410 | }
|
334 | 411 |
|
|
0 commit comments