用法: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();
}
}