用法:https://blog.csdn.net/qq_32868023/article/details/123390627
原理一:https://blog.csdn.net/qq_32868023/article/details/123515652
原理二:https://blog.csdn.net/qq_32868023/article/details/123603241
原理三:https://blog.csdn.net/qq_32868023/article/details/123604439
原理四:https://blog.csdn.net/qq_32868023/article/details/123606236
原理五:https://blog.csdn.net/qq_32868023/article/details/123616110
Binder负责对一个Bindable进行绑定,一个Bindable的属性可能是一个java对象、数组、集合、Map等各种情况,需要递归的去绑定java对象的属性,数组、集合、Map的元素,其流程简单来说就是:从配置属性源中获取到对应的值,将这个值转化为所需要的类型。本节主要分析下获取到值之后如何转换,如何对java对象、数组、集合、Map进行绑定
1、BindConverter
BindConverter负责将配置的Property转换成所需要的类型,具体的转换工作交给它所持有的delegates,看一下delegates的构造函数,delegates包括了三部分
- TypeConverterConversionService,会处理PropertyEditor逻辑
- 参数中的conversionServices,顺着调用链路去看,可以发现conversionServices是从beanFactory里获取的,如果没有配置这个bean的话就没有
- ApplicationConversionService,大部分转换逻辑都是它处理的
final class BindConverter {
private final List<ConversionService> delegates;
private BindConverter(List<ConversionService> conversionServices,
Consumer<PropertyEditorRegistry> propertyEditorInitializer) {
List<ConversionService> delegates = new ArrayList<>();
delegates.add(new TypeConverterConversionService(propertyEditorInitializer));
boolean hasApplication = false;
if (!CollectionUtils.isEmpty(conversionServices)) {
for (ConversionService conversionService : conversionServices) {
delegates.add(conversionService);
hasApplication = hasApplication || conversionService instanceof ApplicationConversionService;
}
}
if (!hasApplication) {
delegates.add(ApplicationConversionService.getSharedInstance());
}
this.delegates = Collections.unmodifiableList(delegates);
}
}
BindConverter的转换逻辑实际上就是遍历了下delegates,将转换工作委托给delegates,具体这些Converter是如何工作的这里就不继续介绍了
private Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
ConversionException failure = null;
for (ConversionService delegate : this.delegates) {
try {
if (delegate.canConvert(sourceType, targetType)) {
return delegate.convert(source, sourceType, targetType);
}
}
catch (ConversionException ex) {
if (failure == null && ex instanceof ConversionFailedException) {
failure = ex;
}
}
}
throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
}
2、AggregateBinder
AggregateBinder是用来绑定数组、集合、Map属性的工具,包括MapBinder、CollectionBinder、ArrayBinder三个
a、AggregateBinder实现
AggregateBinder,bind其实就是个模板方法,调用了bindAggregate抽象方法。
abstract class AggregateBinder<T> {
@SuppressWarnings("unchecked")
final Object bind(ConfigurationPropertyName name, Bindable<?> target, AggregateElementBinder elementBinder) {
Object result = bindAggregate(name, target, elementBinder);
Supplier<?> value = target.getValue();
if (result == null || value == null) {
return result;
}
return merge((Supplier<T>) value, (T) result);
}
}
bind方法里的name和target参数好理解,elementBinder是什么呢?
@FunctionalInterface
interface AggregateElementBinder {
default Object bind(ConfigurationPropertyName name, Bindable<?> target) {
return bind(name, target, null);
}
Object bind(ConfigurationPropertyName name, Bindable<?> target, ConfigurationPropertySource source);
}
AggregateElementBinder就是一个接口,用来绑定数组、集合、Map里面的元素的,这个接口没有实现类,AggregateBinder在调用时,通过lambda的写法传进来一个AggregateElementBinder实例,实例的实现中会调用Binder的bind方法,因为数组、集合、Map里面的元素就是一个普通的Bindable
b、ArrayBinder和CollectionBinder
ArrayBinder和CollectionBinder都继承IndexedElementsBinder。ArrayBinder调了bindIndexed方法然后把List转换成数组
class ArrayBinder extends IndexedElementsBinder<Object> {
@Override
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder) {
IndexedCollectionSupplier result = new IndexedCollectionSupplier(ArrayList::new);
ResolvableType aggregateType = target.getType();
ResolvableType elementType = target.getType().getComponentType();
bindIndexed(name, target, elementBinder, aggregateType, elementType, result);
if (result.wasSupplied()) {
List<Object> list = (List<Object>) result.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;
}
}
CollectionBinder也是调了bindIndexed方法,调用之前用CollectionFactory创建了一下Collection
class CollectionBinder extends IndexedElementsBinder<Collection<Object>> {
@Override
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder) {
Class<?> collectionType = (target.getValue() != null) ? List.class : target.getType().resolve(Object.class);
ResolvableType aggregateType = ResolvableType.forClassWithGenerics(List.class,
target.getType().asCollection().getGenerics());
ResolvableType elementType = target.getType().asCollection().getGeneric();
IndexedCollectionSupplier result = new IndexedCollectionSupplier(
() -> CollectionFactory.createCollection(collectionType, elementType.resolve(), 0));
bindIndexed(name, target, elementBinder, aggregateType, elementType, result);
if (result.wasSupplied()) {
return result.get();
}
return null;
}
}
c、IndexedElementsBinder
IndexedElementsBinder是ArrayBinder和CollectionBinder的父类。
protected final void bindIndexed(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder, ResolvableType aggregateType, ResolvableType elementType,
IndexedCollectionSupplier result) {
for (ConfigurationPropertySource source : getContext().getSources()) {
bindIndexed(source, name, target, elementBinder, result, aggregateType, elementType);
if (result.wasSupplied() && result.get() != null) {
return;
}
}
}
这个bindIndexed遍历了ConfigurationPropertySource,然后调用bindIndexed重载方法绑定
private void bindIndexed(ConfigurationPropertySource source, ConfigurationPropertyName root, Bindable<?> target,
AggregateElementBinder elementBinder, IndexedCollectionSupplier collection, ResolvableType aggregateType,
ResolvableType elementType) {
ConfigurationProperty property = source.getConfigurationProperty(root);
if (property != null) {
getContext().setConfigurationProperty(property);
bindValue(target, collection.get(), aggregateType, elementType, property.getValue());
}
else {
bindIndexed(source, root, elementBinder, collection, elementType);
}
}
bindIndexed的重载先拿到ConfigurationProperty,如果有直接bingValue,如果没有继续调用bindIndexed重载。这里其实是支持了两种写法:
写法一,逗号分隔
demo.list=1,2,3,4,5
写法二
demo.list[0]=1
demo.list[1]=2
demo.list[2]=3
demo.list[3]=4
demo.list[4]=5
这两种写法的效果是一样的,分别看下bindValue和bindIndexed的实现
private void bindValue(Bindable<?> target, Collection<Object> collection, ResolvableType aggregateType,
ResolvableType elementType, Object value) {
if (value == null || value instanceof CharSequence && ((CharSequence) value).length() == 0) {
return;
}
Object aggregate = convert(value, aggregateType, target.getAnnotations());
ResolvableType collectionType = ResolvableType.forClassWithGenerics(collection.getClass(), elementType);
Collection<Object> elements = convert(aggregate, collectionType);
collection.addAll(elements);
}
bindValue就是调用converter将property转换成需要的类型
private void bindIndexed(ConfigurationPropertySource source, ConfigurationPropertyName root,
AggregateElementBinder elementBinder, IndexedCollectionSupplier collection, ResolvableType elementType) {
MultiValueMap<String, ConfigurationPropertyName> knownIndexedChildren = getKnownIndexedChildren(source, root);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
ConfigurationPropertyName name = root.append((i != 0) ? "[" + i + "]" : INDEX_ZERO);
Object value = elementBinder.bind(name, Bindable.of(elementType), source);
if (value == null) {
break;
}
knownIndexedChildren.remove(name.getLastElement(Form.UNIFORM));
collection.get().add(value);
}
assertNoUnboundChildren(source, knownIndexedChildren);
}
bindIndexed方法从index=0开始,追加到root后面,调用elementBinder继续绑定。而elementBinder实际上又是调用的Binder#bind方法,所以集合的元素又可以是一个java对象
d、MapBinder
protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> target,
AggregateElementBinder elementBinder) {
Map<Object, Object> map = CollectionFactory
.createMap((target.getValue() != null) ? Map.class : target.getType().resolve(Object.class), 0);
Bindable<?> resolvedTarget = resolveTarget(target);
boolean hasDescendants = hasDescendants(name);
for (ConfigurationPropertySource source : getContext().getSources()) {
if (!ConfigurationPropertyName.EMPTY.equals(name)) {
ConfigurationProperty property = source.getConfigurationProperty(name);
if (property != null && !hasDescendants) {
return getContext().getConverter().convert(property.getValue(), target);
}
source = source.filter(name::isAncestorOf);
}
new EntryBinder(name, resolvedTarget, elementBinder).bindEntries(source, map);
}
return map.isEmpty() ? null : map;
}
bindAggregate遍历了所有source,对每个source首先还是尝试查找property,如果查找到了就用converter进行转换,由于ApplicationConversionService里没有String到Map的转换,如果没有配置的话走到这里会转换不了抛异常;如果没有找到property,将实例化一个FilteredConfigurationPropertiesSource,这样对source遍历就都是需要的kv了,然后用EntryBinder对Map的Entry进行绑定
void bindEntries(ConfigurationPropertySource source, Map<Object, Object> map) {
if (source instanceof IterableConfigurationPropertySource) {
for (ConfigurationPropertyName name : (IterableConfigurationPropertySource) source) {
Bindable<?> valueBindable = getValueBindable(name);
ConfigurationPropertyName entryName = getEntryName(source, name);
Object key = getContext().getConverter().convert(getKeyName(entryName), this.keyType);
map.computeIfAbsent(key, (k) -> this.elementBinder.bind(entryName, valueBindable));
}
}
}
bindEntries方法遍历了所有的kv(由于source是FilteredConfigurationPropertiesSource,这里遍历出来的kv都是需要绑定的)。for循环里面对key和value进行了绑定,对key是去除keyName,直接convert,那么key必须是BindConverter能直接转换的类型,而对于value,使用elementBinder继续绑定,这一点跟前面数组和集合是一样的
3、DataObjectBinder
DataObjectBinder是一个接口,解决java对象绑定的问题。接口定义如下,有两个方法:bind和create
interface DataObjectBinder {
<T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context,
DataObjectPropertyBinder propertyBinder);
<T> T create(Bindable<T> target, Context context);
}
java对象是怎么绑定的?有两种情况:
- 对于bindMethod=JAVA_BEAN,遍历java对象内的所有属性,先将属性的值绑定出来,然后调用set方法将值设置进去
- 对于bindMethod=VALUE_OBJECT,需要先准备好所有的构造函数参数,然后调用构造函数实例化出来
这两种情况也就对应了DataObjectBinder的两个实现,bind方法name、target、context都好理解,propetyBinder用来绑定每个属性,DataObjectPropertyBinder是一个接口,就一个bindProperty方法,这个接口没有实现类,也是通过lambda写法传进来实例。对于property实际上也是一个普通的Bindable对象,所以其lambda实现中也是调用了Binder#bind方法
interface DataObjectPropertyBinder {
Object bindProperty(String propertyName, Bindable<?> target);
}
看下DataObjectBinder的两个实现:JavaBeanBinder和ValueObjectBinder
a、JavaBeanBinder
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context,
DataObjectPropertyBinder propertyBinder) {
boolean hasKnownBindableProperties = target.getValue() != null && hasKnownBindableProperties(name, context);
Bean<T> bean = Bean.get(target, hasKnownBindableProperties);
if (bean == null) {
return null;
}
BeanSupplier<T> beanSupplier = bean.getSupplier(target);
boolean bound = bind(propertyBinder, bean, beanSupplier, context);
return (bound ? beanSupplier.get() : null);
}
bind实现先通过Bean#get实例化一个bean,这个过程已经把target的属性全部解析好了,调用bind重载方法。这Bean#get解析的过程中,已经将fieldName的驼峰命名法转换成了用’-'来分隔不同单词
private <T> boolean bind(DataObjectPropertyBinder propertyBinder, Bean<T> bean, BeanSupplier<T> beanSupplier,
Context context) {
boolean bound = false;
for (BeanProperty beanProperty : bean.getProperties().values()) {
bound |= bind(beanSupplier, propertyBinder, beanProperty);
context.clearConfigurationProperty();
}
return bound;
}
这个重载方法遍历了Bean解析出来的所有field,继续调用bind重载
private <T> boolean bind(BeanSupplier<T> beanSupplier, DataObjectPropertyBinder propertyBinder,
BeanProperty property) {
String propertyName = property.getName();
ResolvableType type = property.getType();
Supplier<Object> value = property.getValue(beanSupplier);
Annotation[] annotations = property.getAnnotations();
Object bound = propertyBinder.bindProperty(propertyName,
Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
if (bound == null) {
return false;
}
if (property.isSettable()) {
property.setValue(beanSupplier, bound);
}
else if (value == null || !bound.equals(value.get())) {
throw new IllegalStateException("No setter found for property: " + property.getName());
}
return true;
}
这个重载好理解了,调用propertyBinder获取绑定到这个field的值,然后调用set方法set进去。
b、ValueObjectBinder
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Binder.Context context,
DataObjectPropertyBinder propertyBinder) {
ValueObject<T> valueObject = ValueObject.get(target, this.constructorProvider, context);
if (valueObject == null) {
return null;
}
context.pushConstructorBoundTypes(target.getType().resolve());
List<ConstructorParameter> parameters = valueObject.getConstructorParameters();
List<Object> args = new ArrayList<>(parameters.size());
boolean bound = false;
for (ConstructorParameter parameter : parameters) {
Object arg = parameter.bind(propertyBinder);
bound = bound || arg != null;
arg = (arg != null) ? arg : getDefaultValue(context, parameter);
args.add(arg);
}
context.clearConfigurationProperty();
context.popConstructorBoundTypes();
return bound ? valueObject.instantiate(args) : null;
}
这里逻辑也不复杂,解析出构造函数的所有参数ConstructorParameter,for循环通过propertyBinder获取到每个参数绑定到的value,然后将这些value作为参数调用构造函数去实例化对象,完成绑定