Spring Boot @ConfigurationProperties(原理五)

用法: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的元素,其流程简单来说就是:从配置属性源中获取到对应的值,将这个值转化为所需要的类型。本节主要分析下Binder的绑定逻辑和绑定上下文Context

1、Binder

Binder里面的几个重要属性

public class Binder {

	private final Iterable<ConfigurationPropertySource> sources;

	private final PlaceholdersResolver placeholdersResolver;

	private final BindConverter bindConverter;

	private final BindHandler defaultBindHandler;

	private final List<DataObjectBinder> dataObjectBinders;
}
  • sources,可以迭代出ConfigurationPropertySource
  • placeholdersResolver,用于解析配置值里面的placeholder
  • bindConverter,用于转换配置的值
  • defaultBindHandler,默认的BindHandler,如果没传就用默认的
  • dataObjectBinders,用于绑定DataObject

前面分析过,ConfigurationPropertiesBinder在bind和bindOrCreate方法里调用Binder的对应方法,如下

	BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {
		Bindable<?> target = propertiesBean.asBindTarget();
		ConfigurationProperties annotation = propertiesBean.getAnnotation();
		BindHandler bindHandler = getBindHandler(target, annotation);
		return getBinder().bind(annotation.prefix(), target, bindHandler);
	}

	Object bindOrCreate(ConfigurationPropertiesBean propertiesBean) {
		Bindable<?> target = propertiesBean.asBindTarget();
		ConfigurationProperties annotation = propertiesBean.getAnnotation();
		BindHandler bindHandler = getBindHandler(target, annotation);
		return getBinder().bindOrCreate(annotation.prefix(), target, bindHandler);
	}

顺着ConfigurationPropertiesBinder的调用链路,可以找到Binder下面这个重要方法

	private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, boolean create) {
		Assert.notNull(name, "Name must not be null");
		Assert.notNull(target, "Target must not be null");
		handler = (handler != null) ? handler : this.defaultBindHandler;
		Context context = new Context();
		return bind(name, target, handler, context, false, create);
	}

这个方法里面new了一个Context,然后调用了bind的重载。这个bind的重载方法是属性绑定的核心逻辑,它负责对一个Bindable进行绑定,这个Bindable可以是外部调用传进来的Bindable,也可以是绑定过程在为了绑定数组、集合、Map、java属性而生成的Bindable,涉及递归调用,主要处理了几件事情

  • 在Bindable绑定的各个阶段,调用Bindable回调函数
  • 将Bindable分为属性绑定、Aggregate绑定、DataObject绑定等情况,分别调用对应的Binder
  • 绑定方式按JAVA_BEAN和VALUE_OBJECT,分别调用对应的Binder

先一张图分析总体流程

顺着这张图分析代码

	private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,
			boolean allowRecursiveBinding, boolean create) {
		try {
			Bindable<T> replacementTarget = handler.onStart(name, target, context);
			if (replacementTarget == null) {
				return handleBindResult(name, target, handler, context, null, create);
			}
			target = replacementTarget;
			Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
			return handleBindResult(name, target, handler, context, bound, create);
		}
		catch (Exception ex) {
			return handleBindError(name, target, handler, context, ex);
		}
	}

这里面onStart、handleBindResult、handleBindError处理了hanler回调函数,bindObject将Bindable分为几种情况进行绑定,先看下handleBindResult、handleBindError回调函数的处理

	private <T> T handleBindResult(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, Object result, boolean create) throws Exception {
		if (result != null) {
			result = handler.onSuccess(name, target, context, result);
			result = context.getConverter().convert(result, target);
		}
		if (result == null && create) {
			result = create(target, context);
			result = handler.onCreate(name, target, context, result);
			result = context.getConverter().convert(result, target);
			Assert.state(result != null, () -> "Unable to create instance for " + target.getType());
		}
		handler.onFinish(name, target, context, result);
		return context.getConverter().convert(result, target);
	}

handleBindResult调用了onSuccess回调,如果需要创建对象(bindOrCreate方法传进来的create为true)则创建对象并调用onCreate回调,最后调用onFinish回调

	private <T> T handleBindError(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, Exception error) {
		try {
			Object result = handler.onFailure(name, target, context, error);
			return context.getConverter().convert(result, target);
		}
		catch (Exception ex) {
			if (ex instanceof BindException) {
				throw (BindException) ex;
			}
			throw new BindException(name, target, context.getConfigurationProperty(), ex);
		}
	}

handleBindError这里调用了onFailure回调,这个回调里面可以吞掉异常给一个默认值,默认是继续抛出绑定失败
再来看下bindObject方法

	private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, boolean allowRecursiveBinding) {
		ConfigurationProperty property = findProperty(name, target, context);
		if (property == null && context.depth != 0 && containsNoDescendantOf(context.getSources(), name)) {
			return null;
		}
		AggregateBinder<?> aggregateBinder = getAggregateBinder(target, context);
		if (aggregateBinder != null) {
			return bindAggregate(name, target, handler, context, aggregateBinder);
		}
		if (property != null) {
			try {
				return bindProperty(target, context, property);
			}
			catch (ConverterNotFoundException ex) {
				// We might still be able to bind it using the recursive binders
				Object instance = bindDataObject(name, target, handler, context, allowRecursiveBinding);
				if (instance != null) {
					return instance;
				}
				throw ex;
			}
		}
		return bindDataObject(name, target, handler, context, allowRecursiveBinding);
	}

bindObject将Bindable的绑定分成了三种情况

  • 如果这个Bindable是数组、集合、Map(aggregateBinder != null),那么调用bindAggregate
  • 如果配置source中找到了kv,那么开始绑定这个属性,调用bindProperty
  • 否则当作DataObject进行绑定,调用bindDataObject

分别看下bindProperty、bindAggregate、bindDataObject

	private <T> Object bindProperty(Bindable<T> target, Context context, ConfigurationProperty property) {
		context.setConfigurationProperty(property);
		Object result = property.getValue();
		result = this.placeholdersResolver.resolvePlaceholders(result);
		result = context.getConverter().convert(result, target);
		return result;
	}

bindProperty逻辑是最简单的,用placeholdersResolver解析一下placeholder,然后通过Converter转换,就是绑定的结果

	private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, AggregateBinder<?> aggregateBinder) {
		AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
			boolean allowRecursiveBinding = aggregateBinder.isAllowRecursiveBinding(source);
			Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding, false);
			return context.withSource(source, supplier);
		};
		return context.withIncreasedDepth(() -> aggregateBinder.bind(name, target, elementBinder));
	}

bindAggregate全是lambda写法不好理解,bindAggregate做了两件事情
1、lambda写法实例化了一个AggregateElementBinder
2、然后调用了context#withIncreasedDepth,这个方法实际上就是调用了里面的Supplier,也就是调用了aggregateBinder#bind方法进行绑定
所以这里实际上就是调用了AggregateBinder#bind方法,并把elementBinder作为参数传了进去,elementBinder在绑定数组、集合、Map的每一个元素时,会被调用。elementBinder被调用时又做了什么?
elementBinder的lambda实现中,实例化了一个Supplier,这个Supplier调用bind方法去绑定一个element,context#withSource实际上就是调用了这个supplier

	private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,
			Context context, boolean allowRecursiveBinding) {
		if (isUnbindableBean(name, target, context)) {
			return null;
		}
		Class<?> type = target.getType().resolve(Object.class);
		if (!allowRecursiveBinding && context.isBindingDataObject(type)) {
			return null;
		}
		DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
				propertyTarget, handler, context, false, false);
		return context.withDataObject(type, () -> {
			for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {
				Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
				if (instance != null) {
					return instance;
				}
			}
			return null;
		});
	}

bindDataObject跟bindAggregate理解起来差不多,用lambda写法实例化了一个propertyBinder,然后propertyBinder作为参数遍历dataObjectBinders去绑定这个DataObject。dataObjectBinders包括两个:JavaBeanBinder和ValueObjectBinder,对应JAVA_BEAN和VALUE_OBJECT两种绑定方式,这两个dataObjectBinder前面也以及分析过

2、Context

Context是用来保存上下文信息的,考虑一个问题:AggregateBinder会遍历source来绑定elements,绑定elements的elementBinder又是调用的Binder#bind方法,那么当遍历到某个source s来绑定elements时,绑定element的source应当还是这个source s而不是所有的source(为什么?因为AggregateBinder会遍历所有的source,绑定elements又遍历了所有source的话会重复),context就是为了保存此类上下文环境

	final class Context implements BindContext {

		private int depth;

		private final List<ConfigurationPropertySource> source = Arrays.asList((ConfigurationPropertySource) null);

		private int sourcePushCount;

		private final Deque<Class<?>> dataObjectBindings = new ArrayDeque<>();

		private final Deque<Class<?>> constructorBindings = new ArrayDeque<>();

		private ConfigurationProperty configurationProperty;
    }
  • depth:绑定深度,每递归绑定一次深度+1,递归出来深度-1
  • source和sourcePushCount,保存当前正在绑定的source
  • dataObjectBindings:正在绑定的DataObject的类型
  • constructorBindings:正在绑定的VALUE_OBJECT的类型
  • configurationProperty:正在绑定的kv

a、depth

		private <T> T withIncreasedDepth(Supplier<T> supplier) {
			increaseDepth();
			try {
				return supplier.get();
			}
			finally {
				decreaseDepth();
			}
		}

深度为0表示正在绑定的是最外层的对象,BindHandler会以此来判断。withIncreasedDepth方法很简单,调用bindAggrate和bindDataObject都会增加深度

	private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, AggregateBinder<?> aggregateBinder) {
		AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
			boolean allowRecursiveBinding = aggregateBinder.isAllowRecursiveBinding(source);
			Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding, false);
			return context.withSource(source, supplier);
		};
		return context.withIncreasedDepth(() -> aggregateBinder.bind(name, target, elementBinder));
	}
	private Object bindDataObject(ConfigurationPropertyName name, Bindable<?> target, BindHandler handler,
			Context context, boolean allowRecursiveBinding) {
		if (isUnbindableBean(name, target, context)) {
			return null;
		}
		Class<?> type = target.getType().resolve(Object.class);
		if (!allowRecursiveBinding && context.isBindingDataObject(type)) {
			return null;
		}
		DataObjectPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(name.append(propertyName),
				propertyTarget, handler, context, false, false);
		return context.withDataObject(type, () -> {
			for (DataObjectBinder dataObjectBinder : this.dataObjectBinders) {
				Object instance = dataObjectBinder.bind(name, target, context, propertyBinder);
				if (instance != null) {
					return instance;
				}
			}
			return null;
		});
	}

context#withdataObject也会增加深度,后面再看

b、source

withSource保存了source同时还递增了sourcePushCount

		private <T> T withSource(ConfigurationPropertySource source, Supplier<T> supplier) {
			if (source == null) {
				return supplier.get();
			}
			this.source.set(0, source);
			this.sourcePushCount++;
			try {
				return supplier.get();
			}
			finally {
				this.sourcePushCount--;
			}
		}

Binder在遍历source时并没有直接取Binder的sources属性,而是通过context#getSources来获取

		public Iterable<ConfigurationPropertySource> getSources() {
			if (this.sourcePushCount > 0) {
				return this.source;
			}
			return Binder.this.sources;
		}

bindAggregate时的elementBinder会通过withResource保存正在绑定的source

	private <T> Object bindAggregate(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
			Context context, AggregateBinder<?> aggregateBinder) {
		AggregateElementBinder elementBinder = (itemName, itemTarget, source) -> {
			boolean allowRecursiveBinding = aggregateBinder.isAllowRecursiveBinding(source);
			Supplier<?> supplier = () -> bind(itemName, itemTarget, handler, context, allowRecursiveBinding, false);
			return context.withSource(source, supplier);
		};
		return context.withIncreasedDepth(() -> aggregateBinder.bind(name, target, elementBinder));
	}

c、dataObjectBindings

withDataObject缓存正在绑定的DataObject的类型之外,还会递增绑定深度

		private <T> T withDataObject(Class<?> type, Supplier<T> supplier) {
			this.dataObjectBindings.push(type);
			try {
				return withIncreasedDepth(supplier);
			}
			finally {
				this.dataObjectBindings.pop();
			}
		}

其作用就是判断当前是否正在绑定DataObject

		private boolean isBindingDataObject(Class<?> type) {
			return this.dataObjectBindings.contains(type);
		}

使用地方就是bindDataObject方法

d、constructorBindings

pushConstructorBoundTypes缓存正在进行VALUE_OBJECT绑定的类型

		void pushConstructorBoundTypes(Class<?> value) {
			this.constructorBindings.push(value);
		}

作用就是判断当前是否正在VALUE_OBEJCT绑定

		boolean isNestedConstructorBinding() {
			return !this.constructorBindings.isEmpty();
		}

用处就是在ValueObjectBinder#bind过程中

	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;
	}

e、configurationProperty

缓存正在绑定的属性的kv,在bindProperty之前会set

		void setConfigurationProperty(ConfigurationProperty configurationProperty) {
			this.configurationProperty = configurationProperty;
		}

作用就是当绑定出错时,便于handler获取正在绑定的值,生成报错信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值