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

本节主要介绍ConfigurationPropertiesBinder相关背景及实现。ConfigurationPropertiesBinder将@ConfigurationProperties的多种使用方式统一起来,实例化Binder并将属性绑定的具体任务交给Binder,起到一个桥梁作用

1、调用链路

前面分析ConfigurationPropertiesBeanRegistrar的时候注意到,ConfigurationPropertiesBeanRegistrar在注册bean时,对于bindMethod=VALUE_OBJECT的类,会提供一个instanceSupplier,而对于bindMethod=JAVA_BEAN则不会,这两者的处理是有区别的,分别来看

a、JAVA_BEAN的属性绑定

ConfigurationPropertiesBindingPostProcessor处理JAVA_BEAN的属性绑定,关键代码如下,逻辑就是为每个需要绑定的bean构造了一个ConfigurationPropertiesBean,然后调用了ConfigurationPropertiesBinder这个binder进行属性绑定,bind方法里会判断是否是VALUE_OBJECT,如果是直接返回不处理

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
		return bean;
	}

	private void bind(ConfigurationPropertiesBean bean) {
		if (bean == null || hasBoundValueObject(bean.getName())) {
			return;
		}
		Assert.state(bean.getBindMethod() == BindMethod.JAVA_BEAN, "Cannot bind @ConfigurationProperties for bean '"
				+ bean.getName() + "'. Ensure that @ConstructorBinding has not been applied to regular bean");
		try {
			this.binder.bind(bean);
		}
		catch (Exception ex) {
			throw new ConfigurationPropertiesBindException(bean, ex);
		}
	}

b、VALUE_OBJECT的属性绑定

ConfigurationPropertiesBeanRegistrar在为VALUE_OBJECT创建BeanDefinition时提供了一个instanceSupplier,这个instanceSupplier里面调用ConfigurationPropertiesBinder进行属性绑定

final class ConfigurationPropertiesBeanRegistrar {
    
    private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
		BindMethod bindMethod = BindMethod.forType(type);
		RootBeanDefinition definition = new RootBeanDefinition(type);
		definition.setAttribute(BindMethod.class.getName(), bindMethod);
		if (bindMethod == BindMethod.VALUE_OBJECT) {
			definition.setInstanceSupplier(() -> createValueObject(beanName, type));
		}
		return definition;
	}

	private Object createValueObject(String beanName, Class<?> beanType) {
		ConfigurationPropertiesBean bean = ConfigurationPropertiesBean.forValueObject(beanType, beanName);
		ConfigurationPropertiesBinder binder = ConfigurationPropertiesBinder.get(this.beanFactory);
		try {
			return binder.bindOrCreate(bean);
		}
		catch (Exception ex) {
			throw new ConfigurationPropertiesBindException(bean, ex);
		}
	}
}

2、ConfigurationPropertiesBinder

ConfigurationPropertiesBinder对外提供bind和bindOrCreate两个方法,这两个方法本身很简单,拿到bindTarget和annotation,创建BindHandler,然后创建Binder,将绑定细节交给Binder。主要有几个问题

  • ConfigurationPropertiesBean是什么
  • Bindable是什么
  • BindHandler是什么,getBindHandler逻辑是怎样的

下面分别看下这三个问题,至于getBinder方法,这里会实例化Binder,后面分析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);
	}

a、Bindable

Bindable封装的是能被Binder进行属性绑定的source(可以指一个类(需要先创建一个instance)、也可以指一个instance、还可以是instance里的一个需要被绑定的属性),里面包含这个类、instance的Supplier、注解、绑定限制,属性如下:

public final class Bindable<T> {
	private final ResolvableType type;

	private final ResolvableType boxedType;

	private final Supplier<T> value;

	private final Annotation[] annotations;

	private final EnumSet<BindRestriction> bindRestrictions;
}

type、boxedType就是解析过的类型,value是一个Supplier、可能是null,annotations就是这个类上的注解,bindRestrictions是绑定限制,目前BindRestriction只有一个枚举值NO_DIRECT_PROPERTY,不要直接绑定Spring bean,而对其属性进行绑定(直接对bean绑定的话必须要实例化,但bean必须是spring来实例化而不是Binder)

	public enum BindRestriction {

		NO_DIRECT_PROPERTY
	}

这个类提供了一些of方法和with方法来方便的创建bindable对象
of方法

	public static <T> Bindable<T> of(Class<T> type) {
		Assert.notNull(type, "Type must not be null");
		return of(ResolvableType.forClass(type));
	}

	public static <T> Bindable<T> ofInstance(T instance) {
		Assert.notNull(instance, "Instance must not be null");
		Class<T> type = (Class<T>) instance.getClass();
		return of(type).withExistingValue(instance);
	}

    public static <E> Bindable<List<E>> listOf(Class<E> elementType) {
		return of(ResolvableType.forClassWithGenerics(List.class, elementType));
	}

	public static <T> Bindable<T> of(ResolvableType type) {
		Assert.notNull(type, "Type must not be null");
		ResolvableType boxedType = box(type);
		return new Bindable<>(type, boxedType, null, NO_ANNOTATIONS, NO_BIND_RESTRICTIONS);
	}

with方法

	public Bindable<T> withAnnotations(Annotation... annotations) {
		return new Bindable<>(this.type, this.boxedType, this.value,
				(annotations != null) ? annotations : NO_ANNOTATIONS, NO_BIND_RESTRICTIONS);
	}

	public Bindable<T> withExistingValue(T existingValue) {
		Assert.isTrue(
				existingValue == null || this.type.isArray() || this.boxedType.resolve().isInstance(existingValue),
				() -> "ExistingValue must be an instance of " + this.type);
		Supplier<T> value = (existingValue != null) ? () -> existingValue : null;
		return new Bindable<>(this.type, this.boxedType, value, this.annotations, this.bindRestrictions);
	}

	public Bindable<T> withBindRestrictions(BindRestriction... additionalRestrictions) {
		EnumSet<BindRestriction> bindRestrictions = EnumSet.copyOf(this.bindRestrictions);
		bindRestrictions.addAll(Arrays.asList(additionalRestrictions));
		return new Bindable<>(this.type, this.boxedType, this.value, this.annotations, bindRestrictions);
	}

	public Bindable<T> withSuppliedValue(Supplier<T> suppliedValue) {
		return new Bindable<>(this.type, this.boxedType, suppliedValue, this.annotations, this.bindRestrictions);
	}

b、ConfigurationPropertiesBean

ConfigurationPropertiesBean封装了一个需要绑定的bean,跟bindable的区别是ConfigurationPropertiesBean跟spring里bean是对应的,而bindable指的是一个能被绑定的source,可以是类、instance、或者一个field,bindable可以脱离spring使用

public final class ConfigurationPropertiesBean {

	private final String name;

	private final Object instance;

	private final ConfigurationProperties annotation;

	private final Bindable<?> bindTarget;

	private final BindMethod bindMethod;
}

name就是beanName,instance就是bean实例,bindMethod=VALUE_OBJECT时instance为null

JAVA_BEAN ConfigurationPropertiesBean的创建

ConfigurationPropertiesBindingPostProcessor里处理每个bean时调用ConfigurationPropertiesBean#get来创建

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
		return bean;
	}

创建过程如下,调用了Bindable bindTarget = Bindable.of(bindType).withAnnotations(annotations);创建了bindTarget,然后new了一个ConfigurationPropertiesBean

	public static ConfigurationPropertiesBean get(ApplicationContext applicationContext, Object bean, String beanName) {
		Method factoryMethod = findFactoryMethod(applicationContext, beanName);
		return create(beanName, bean, bean.getClass(), factoryMethod);
	}

	private static ConfigurationPropertiesBean create(String name, Object instance, Class<?> type, Method factory) {
		ConfigurationProperties annotation = findAnnotation(instance, type, factory, ConfigurationProperties.class);
		if (annotation == null) {
			return null;
		}
		Validated validated = findAnnotation(instance, type, factory, Validated.class);
		Annotation[] annotations = (validated != null) ? new Annotation[] { annotation, validated }
				: new Annotation[] { annotation };
		ResolvableType bindType = (factory != null) ? ResolvableType.forMethodReturnType(factory)
				: ResolvableType.forClass(type);
		Bindable<Object> bindTarget = Bindable.of(bindType).withAnnotations(annotations);
		if (instance != null) {
			bindTarget = bindTarget.withExistingValue(instance);
		}
		return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
	}
VALUE_OBJECT ConfigurationPropertiesBean的创建

ConfigurationPropertiesBeanRegistrar创建beanDefinition的instanceSupplier里,创建了ConfigurationPropertiesBean

	private Object createValueObject(String beanName, Class<?> beanType) {
		ConfigurationPropertiesBean bean = ConfigurationPropertiesBean.forValueObject(beanType, beanName);
		ConfigurationPropertiesBinder binder = ConfigurationPropertiesBinder.get(this.beanFactory);
		try {
			return binder.bindOrCreate(bean);
		}
		catch (Exception ex) {
			throw new ConfigurationPropertiesBindException(bean, ex);
		}
	}

forValueObject里面还是调用了create方式,只是这是instance时null

	static ConfigurationPropertiesBean forValueObject(Class<?> beanClass, String beanName) {
		ConfigurationPropertiesBean propertiesBean = create(beanName, null, beanClass, null);
		Assert.state(propertiesBean != null && propertiesBean.getBindMethod() == BindMethod.VALUE_OBJECT,
				() -> "Bean '" + beanName + "' is not a @ConfigurationProperties value object");
		return propertiesBean;
	}

c、BindHandler

bindHandler规定了5个回调方法,在对Bindable进行绑定的各个时期会调用回调方法,并采用责任链设计模式,可以自由组合多个handler来实现多种功能,这个接口每个方法都有default实现。这里先分析这种设计模式,何时回调这些方法在分析Binder时一并分析

public interface BindHandler {

	BindHandler DEFAULT = new BindHandler() {

	};

	default <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> target, BindContext context) {
		return target;
	}

	default Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
		return result;
	}

	default Object onCreate(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
		return result;
	}

	default Object onFailure(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Exception error)
			throws Exception {
		throw error;
	}

	default void onFinish(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result)
			throws Exception {
	}
}

AbstractBindHandler持有一个默认的parent,对onCreate之外的4个方法重载了实现,调用了parent的相应方法。那么子类再实现这些方法是,只要调用super.onXXX,就能实现责任链组合不同handler的功能,实际上有些子类实现可以不调用super.onXXX,这样责任链到这里就结束了,所有parent的该方法都不会被调用

public abstract class AbstractBindHandler implements BindHandler {

	private final BindHandler parent;

	public AbstractBindHandler() {
		this(BindHandler.DEFAULT);
	}

	public AbstractBindHandler(BindHandler parent) {
		Assert.notNull(parent, "Parent must not be null");
		this.parent = parent;
	}

	@Override
	public <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> 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);
	}
}

看几个具体的实现

IgnoreTopLevelConverterNotFoundBindHandler

IgnoreTopLevelConverterNotFoundBindHandler重载了onFailure,如果当前正在绑定最外层的bean、并且抛出了ConverterNotFoundException,则不抛出这个异常。这也我也看懂到底什么情况下会走到这

public class IgnoreTopLevelConverterNotFoundBindHandler extends AbstractBindHandler {
    // construstors

	@Override
	public Object onFailure(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Exception error)
			throws Exception {
		if (context.getDepth() == 0 && error instanceof ConverterNotFoundException) {
			return null;
		}
		throw error;
	}
}

ConfigurationPropertiesBindHandler

这个作用是看绑定的Target上有没有@ConfigurationProperties注解,有的话加入绑定限制:NO_DIRECT_PROPERTY,因为不管BindMethod是JAVA_BEAN还是VALUE_OBJECT,都是外部来创建需要绑定的对象的

	private static class ConfigurationPropertiesBindHandler extends AbstractBindHandler {

		ConfigurationPropertiesBindHandler(BindHandler handler) {
			super(handler);
		}

		@Override
		public <T> Bindable<T> onStart(ConfigurationPropertyName name, Bindable<T> target, BindContext context) {
			return isConfigurationProperties(target.getType().resolve())
					? target.withBindRestrictions(BindRestriction.NO_DIRECT_PROPERTY) : target;
		}

		private boolean isConfigurationProperties(Class<?> target) {
			return target != null && MergedAnnotations.from(target).isPresent(ConfigurationProperties.class);
		}

	}

IgnoreErrorsBindHandler

IgnoreErrorsBindHandler是对@ConfigurationProperties#ignoreInvalidFields()=false时的支持,当某个属性转换错误时吞掉异常返回null

public class IgnoreErrorsBindHandler extends AbstractBindHandler {

	public IgnoreErrorsBindHandler() {
	}

	public IgnoreErrorsBindHandler(BindHandler parent) {
		super(parent);
	}

	@Override
	public Object onFailure(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Exception error)
			throws Exception {
		return (target.getValue() != null) ? target.getValue().get() : null;
	}
}

NoUnboundElementsBindHandler

NoUnboundElementsBindHandler是对@ConfigurationProperties#ignoreUnknownFields()=false的支持。忽略细节,其中boundNames记录绑定过的.properties里的属性名,重载了onSuccess方法和onFinish方法,每次绑定成功后将绑定的属性名保存到boundNames,绑定结束后看当前是否在绑定最顶层bean,如果是的话,从context里拿出所有的propertySource,checkNoUnboundElements方法看有没有没绑定的属性,有的话会抛出异常

public class NoUnboundElementsBindHandler extends AbstractBindHandler {

	private final Set<ConfigurationPropertyName> boundNames = new HashSet<>();

	@Override
	public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
		this.boundNames.add(name);
		return super.onSuccess(name, target, context, result);
	}
    
	@Override
	public void onFinish(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result)
			throws Exception {
		if (context.getDepth() == 0) {
			checkNoUnboundElements(name, context);
		}
	}
}

ValidationBindHandler

ValidationBindHandler支持了绑定时候对属性值的验证,支持jsr303和自定义的验证。属性validators保存了用来对值进行验证的Validator。onFinish的重载中对值进行了验证,遍历了Validator数组对该值进行验证,有不满足限制条件的将会抛出异常

public class ValidationBindHandler extends AbstractBindHandler {

	private final Validator[] validators;

	public ValidationBindHandler(BindHandler parent, Validator... validators) {
		super(parent);
		this.validators = validators;
	}

    @Override
	public void onFinish(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result)
			throws Exception {
		validate(name, target, context, result);
		super.onFinish(name, target, context, result);
	}
    
    	private void validate(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
		if (this.exception == null) {
			Object validationTarget = getValidationTarget(target, context, result);
			Class<?> validationType = target.getBoxedType().resolve();
			if (validationTarget != null) {
				validateAndPush(name, validationTarget, validationType);
			}
		}
		if (context.getDepth() == 0 && this.exception != null) {
			throw this.exception;
		}
	}

	private Object getValidationTarget(Bindable<?> target, BindContext context, Object result) {
		if (result != null) {
			return result;
		}
		if (context.getDepth() == 0 && target.getValue() != null) {
			return target.getValue().get();
		}
		return null;
	}

	private void validateAndPush(ConfigurationPropertyName name, Object target, Class<?> type) {
		ValidationResult result = null;
		for (Validator validator : this.validators) {
			if (validator.supports(type)) {
				result = (result != null) ? result : new ValidationResult(name, target);
				validator.validate(target, result);
			}
		}
		if (result != null && result.hasErrors()) {
			this.exception = new BindValidationException(result.getValidationErrors());
		}
	}
}

BoundPropertiesTrackingBindHandler

BoundPropertiesTrackingBindHandler用来跟踪绑定成功的属性,重载了onSuccess方法,会调用consumer

public class BoundPropertiesTrackingBindHandler extends AbstractBindHandler {

	private final Consumer<ConfigurationProperty> consumer;

	public BoundPropertiesTrackingBindHandler(Consumer<ConfigurationProperty> consumer) {
		Assert.notNull(consumer, "Consumer must not be null");
		this.consumer = consumer;
	}

	@Override
	public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
		if (context.getConfigurationProperty() != null && name.equals(context.getConfigurationProperty().getName())) {
			this.consumer.accept(context.getConfigurationProperty());
		}
		return super.onSuccess(name, target, context, result);
	}

}

spring里注册了有一个BoundConfigurationProperties,并且ConfigurationPropertiesBinder#getBindHandler在创建BindHandler时用到了,BoundConfigurationProperties#add方法作为方法引用传给Tracker

	private IgnoreTopLevelConverterNotFoundBindHandler getHandler() {
		BoundConfigurationProperties bound = BoundConfigurationProperties.get(this.applicationContext);
		return (bound != null)
				? new IgnoreTopLevelConverterNotFoundBindHandler(new BoundPropertiesTrackingBindHandler(bound::add))
				: new IgnoreTopLevelConverterNotFoundBindHandler();
	}

BoundConfigurationProperties如下,也就是每次绑定成功的属性都会记录到BoundConfigurationProperties#properties里,可以通过get或getAll查询哪些属性被绑定了

public class BoundConfigurationProperties {

	private Map<ConfigurationPropertyName, ConfigurationProperty> properties = new LinkedHashMap<>();

	void add(ConfigurationProperty configurationProperty) {
		this.properties.put(configurationProperty.getName(), configurationProperty);
	}
    
    public ConfigurationProperty get(ConfigurationPropertyName name) {
		return this.properties.get(name);
	}
    
	public Map<ConfigurationPropertyName, ConfigurationProperty> getAll() {
		return Collections.unmodifiableMap(this.properties);
	}
}

BindHandler的组装

ConfigurationPropertiesBinder#getBindHandler为bindTarget创建BindHandler

  • getHandler创建了IgnoreTopLevelConverterNotFoundBindHandler,BoundPropertiesTrackingBindHandler作为parent,可以记录所有绑定成功的属性
  • 如果开启了ignoreInvalidFields,组装上IgnoreErrorsBindHandler
  • 如果关闭了ignoreUnknownFields,组装上NoUnboundElementsBindHandler
  • 如果有Validator,组装上ValidationBindHandler
  • 最后提供了扩展机制,ConfigurationPropertiesBindHandlerAdvisor可以继续组装handler
	private <T> BindHandler getBindHandler(Bindable<T> target, ConfigurationProperties annotation) {
		List<Validator> validators = getValidators(target);
		BindHandler handler = getHandler();
		handler = new ConfigurationPropertiesBindHandler(handler);
		if (annotation.ignoreInvalidFields()) {
			handler = new IgnoreErrorsBindHandler(handler);
		}
		if (!annotation.ignoreUnknownFields()) {
			UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter();
			handler = new NoUnboundElementsBindHandler(handler, filter);
		}
		if (!validators.isEmpty()) {
			handler = new ValidationBindHandler(handler, validators.toArray(new Validator[0]));
		}
		for (ConfigurationPropertiesBindHandlerAdvisor advisor : getBindHandlerAdvisors()) {
			handler = advisor.apply(handler);
		}
		return handler;
	}

	private IgnoreTopLevelConverterNotFoundBindHandler getHandler() {
		BoundConfigurationProperties bound = BoundConfigurationProperties.get(this.applicationContext);
		return (bound != null)
				? new IgnoreTopLevelConverterNotFoundBindHandler(new BoundPropertiesTrackingBindHandler(bound::add))
				: new IgnoreTopLevelConverterNotFoundBindHandler();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值