-
Notifications
You must be signed in to change notification settings - Fork 40.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for configuration properties binding
Create a new `Binder` class specifically designed to bind properties from one or more `ConfigurationPropertySources` to an object. The binder provides a replacement for `RelaxedBinder` and attempts to fix the limitations of the previous solution. Closes gh-8868
- Loading branch information
Showing
64 changed files
with
7,498 additions
and
0 deletions.
There are no files selected for viewing
73 changes: 73 additions & 0 deletions
73
...t/src/main/java/org/springframework/boot/context/properties/bind/AbstractBindHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* Copyright 2012-2017 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.context.properties.bind; | ||
|
||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* Abstract base class for {@link BindHandler} implementations. | ||
* | ||
* @author Phillip Webb | ||
* @author Madhura Bhave | ||
* @since 2.0.0 | ||
*/ | ||
public abstract class AbstractBindHandler implements BindHandler { | ||
|
||
private final BindHandler parent; | ||
|
||
/** | ||
* Create a new binding handler instance. | ||
*/ | ||
public AbstractBindHandler() { | ||
this(BindHandler.DEFAULT); | ||
} | ||
|
||
/** | ||
* Create a new binding handler instance with a specific parent. | ||
* @param parent the parent handler | ||
*/ | ||
public AbstractBindHandler(BindHandler parent) { | ||
Assert.notNull(parent, "Parent must not be null"); | ||
this.parent = parent; | ||
} | ||
|
||
@Override | ||
public boolean onStart(ConfigurationPropertyName name, Bindable<?> target, | ||
BindContext context) { | ||
return this.parent.onStart(name, target, context); | ||
} | ||
|
||
@Override | ||
public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, | ||
BindContext context, Object result) { | ||
return this.parent.onSuccess(name, target, context, result); | ||
} | ||
|
||
@Override | ||
public Object onFailure(ConfigurationPropertyName name, Bindable<?> target, | ||
BindContext context, Exception error) throws Exception { | ||
return this.parent.onFailure(name, target, context, error); | ||
} | ||
|
||
@Override | ||
public void onFinish(ConfigurationPropertyName name, Bindable<?> target, | ||
BindContext context, Object result) throws Exception { | ||
this.parent.onFinish(name, target, context, result); | ||
} | ||
|
||
} |
130 changes: 130 additions & 0 deletions
130
...-boot/src/main/java/org/springframework/boot/context/properties/bind/AggregateBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* | ||
* Copyright 2012-2017 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.context.properties.bind; | ||
|
||
import java.util.function.Supplier; | ||
|
||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; | ||
import org.springframework.core.ResolvableType; | ||
|
||
/** | ||
* Internal strategy used by {@link Binder} to bind aggregates (Maps, Lists, Arrays). | ||
* | ||
* @param <T> the type being bound | ||
* @author Phillip Webb | ||
* @author Madhura Bhave | ||
*/ | ||
abstract class AggregateBinder<T> { | ||
|
||
private final BindContext context; | ||
|
||
AggregateBinder(BindContext context) { | ||
this.context = context; | ||
} | ||
|
||
/** | ||
* Perform binding for the aggregate. | ||
* @param name the configuration property name to bind | ||
* @param target the target to bind | ||
* @param itemBinder an item binder | ||
* @return the bound aggregate or null | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public final Object bind(ConfigurationPropertyName name, Bindable<?> target, | ||
AggregateElementBinder itemBinder) { | ||
Supplier<?> value = target.getValue(); | ||
Class<?> type = (value == null ? target.getType().resolve() | ||
: ResolvableType.forClass(AggregateBinder.class, getClass()) | ||
.resolveGeneric()); | ||
Object result = bind(name, target, itemBinder, type); | ||
if (result == null || value == null || value.get() == null) { | ||
return result; | ||
} | ||
return merge((T) value.get(), (T) result); | ||
} | ||
|
||
/** | ||
* Perform the actual aggregate binding. | ||
* @param name the configuration property name to bind | ||
* @param target the target to bind | ||
* @param elementBinder an element binder | ||
* @param type the aggregate actual type to use | ||
* @return the bound result | ||
*/ | ||
protected abstract Object bind(ConfigurationPropertyName name, Bindable<?> target, | ||
AggregateElementBinder elementBinder, Class<?> type); | ||
|
||
/** | ||
* Merge any additional elements into the existing aggregate. | ||
* @param existing the existing value | ||
* @param additional the additional elements to merge | ||
* @return the merged result | ||
*/ | ||
protected abstract T merge(T existing, T additional); | ||
|
||
/** | ||
* Return the context being used by this binder. | ||
* @return the context | ||
*/ | ||
protected final BindContext getContext() { | ||
return this.context; | ||
} | ||
|
||
/** | ||
* Roll up the given name to the first element below the root. For example a name of | ||
* {@code foo.bar.baz} rolled up to the root {@code foo} would be {@code foo.bar}. | ||
* @param name the name to roll up | ||
* @param root the root name | ||
* @return the rolled up name or {@code null} | ||
*/ | ||
protected final ConfigurationPropertyName rollUp(ConfigurationPropertyName name, | ||
ConfigurationPropertyName root) { | ||
while (name != null && (name.getParent() != null) | ||
&& (!root.equals(name.getParent()))) { | ||
name = name.getParent(); | ||
} | ||
return name; | ||
} | ||
|
||
/** | ||
* Internal class used to supply the aggregate and cache the value. | ||
* @param <T> The aggregate type | ||
*/ | ||
protected static class AggregateSupplier<T> { | ||
|
||
private final Supplier<T> supplier; | ||
|
||
private T supplied; | ||
|
||
public AggregateSupplier(Supplier<T> supplier) { | ||
this.supplier = supplier; | ||
} | ||
|
||
public T get() { | ||
if (this.supplied == null) { | ||
this.supplied = this.supplier.get(); | ||
} | ||
return this.supplied; | ||
} | ||
|
||
public boolean wasSupplied() { | ||
return this.supplied != null; | ||
} | ||
|
||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
...rc/main/java/org/springframework/boot/context/properties/bind/AggregateElementBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* | ||
* Copyright 2012-2017 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.context.properties.bind; | ||
|
||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; | ||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; | ||
|
||
/** | ||
* Binder that can be used by {@link AggregateBinder} implementations to recursively bind | ||
* elements. | ||
* | ||
* @author Phillip Webb | ||
* @author Madhura Bhave | ||
*/ | ||
@FunctionalInterface | ||
interface AggregateElementBinder { | ||
|
||
/** | ||
* Bind the given name to a target bindable. | ||
* @param name the name to bind | ||
* @param target the target bindable | ||
* @return a bound object or {@code null} | ||
*/ | ||
default Object bind(ConfigurationPropertyName name, Bindable<?> target) { | ||
return bind(name, target, null); | ||
} | ||
|
||
/** | ||
* Bind the given name to a target bindable using optionally limited to a single | ||
* source. | ||
* @param name the name to bind | ||
* @param target the target bindable | ||
* @param source the source of the elements or {@code null} to use all sources | ||
* @return a bound object or {@code null} | ||
*/ | ||
Object bind(ConfigurationPropertyName name, Bindable<?> target, | ||
ConfigurationPropertySource source); | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ArrayBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright 2012-2017 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.context.properties.bind; | ||
|
||
import java.lang.reflect.Array; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; | ||
import org.springframework.core.ResolvableType; | ||
|
||
/** | ||
* {@link AggregateBinder} for arrays. | ||
* | ||
* @author Phillip Webb | ||
* @author Madhura Bhave | ||
*/ | ||
class ArrayBinder extends IndexedElementsBinder<Object> { | ||
|
||
ArrayBinder(BindContext context) { | ||
super(context); | ||
} | ||
|
||
@Override | ||
protected Object bind(ConfigurationPropertyName name, Bindable<?> target, | ||
AggregateElementBinder elementBinder, Class<?> type) { | ||
IndexedCollectionSupplier collection = new IndexedCollectionSupplier( | ||
ArrayList::new); | ||
ResolvableType elementType = target.getType().getComponentType(); | ||
bindIndexed(name, target, elementBinder, collection, target.getType(), | ||
elementType); | ||
if (collection.wasSupplied()) { | ||
List<Object> list = (List<Object>) collection.get(); | ||
Object array = Array.newInstance(elementType.resolve(), list.size()); | ||
for (int i = 0; i < list.size(); i++) { | ||
Array.set(array, i, list.get(i)); | ||
} | ||
return array; | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
protected Object merge(Object existing, Object additional) { | ||
return additional; | ||
} | ||
|
||
} |
43 changes: 43 additions & 0 deletions
43
spring-boot/src/main/java/org/springframework/boot/context/properties/bind/BeanBinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* Copyright 2012-2017 the original author or authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.springframework.boot.context.properties.bind; | ||
|
||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource; | ||
|
||
/** | ||
* Internal strategy used by {@link Binder} to bind beans. | ||
* | ||
* @author Phillip Webb | ||
* @author Madhura Bhave | ||
*/ | ||
interface BeanBinder { | ||
|
||
/** | ||
* Return a bound bean instance or {@code null} if the {@link BeanBinder} does not | ||
* support the specified {@link Bindable}. | ||
* @param target the binable to bind | ||
* @param hasKnownBindableProperties if this binder has known bindable elements. If | ||
* names from underlying {@link ConfigurationPropertySource} cannot be iterated this | ||
* method can be {@code false}, even though binding may ultimately succeed. | ||
* @param propertyBinder property binder | ||
* @param <T> The source type | ||
* @return a bound instance or {@code null} | ||
*/ | ||
<T> T bind(Bindable<T> target, boolean hasKnownBindableProperties, | ||
BeanPropertyBinder propertyBinder); | ||
|
||
} |
Oops, something went wrong.