本文次要钻研一下springboot的ConfigurationProperties的绑定

ConfigurationPropertiesBindingPostProcessor

org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java

/** * {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with * {@link ConfigurationProperties @ConfigurationProperties}. * * @author Dave Syer * @author Phillip Webb * @author Christian Dupuis * @author Stephane Nicoll * @author Madhura Bhave * @since 1.0.0 */public class ConfigurationPropertiesBindingPostProcessor        implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware, InitializingBean {    /**     * The bean name that this post-processor is registered with.     */    public static final String BEAN_NAME = ConfigurationPropertiesBindingPostProcessor.class.getName();    private ApplicationContext applicationContext;    private BeanDefinitionRegistry registry;    private ConfigurationPropertiesBinder binder;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    @Override    public void afterPropertiesSet() throws Exception {        // We can't use constructor injection of the application context because        // it causes eager factory bean initialization        this.registry = (BeanDefinitionRegistry) this.applicationContext.getAutowireCapableBeanFactory();        this.binder = ConfigurationPropertiesBinder.get(this.applicationContext);    }    @Override    public int getOrder() {        return Ordered.HIGHEST_PRECEDENCE + 1;    }    @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);        }    }    private boolean hasBoundValueObject(String beanName) {        return this.registry.containsBeanDefinition(beanName) && this.registry                .getBeanDefinition(beanName) instanceof ConfigurationPropertiesValueObjectBeanDefinition;    }    /**     * Register a {@link ConfigurationPropertiesBindingPostProcessor} bean if one is not     * already registered.     * @param registry the bean definition registry     * @since 2.2.0     */    public static void register(BeanDefinitionRegistry registry) {        Assert.notNull(registry, "Registry must not be null");        if (!registry.containsBeanDefinition(BEAN_NAME)) {            BeanDefinition definition = BeanDefinitionBuilder                    .genericBeanDefinition(ConfigurationPropertiesBindingPostProcessor.class,                            ConfigurationPropertiesBindingPostProcessor::new)                    .getBeanDefinition();            definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);            registry.registerBeanDefinition(BEAN_NAME, definition);        }        ConfigurationPropertiesBinder.register(registry);    }}
ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四个接口;其getOrder办法返回的是Ordered.HIGHEST_PRECEDENCE + 1即仅次于最高的优先级;其postProcessBeforeInitialization办法次要是执行bind办法(先通过ConfigurationPropertiesBean.get获取ConfigurationPropertiesBean,再通过binder进行bind);其afterPropertiesSet次要是获取BeanDefinitionRegistry与ConfigurationPropertiesBinder

ConfigurationPropertiesBean.get

org/springframework/boot/context/properties/ConfigurationPropertiesBean.java

    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);    }
get办法次要是获取工厂办法,之后获取annotation,获取bindTarget,最初创立ConfigurationPropertiesBean

ConfigurationPropertiesBean

org/springframework/boot/context/properties/ConfigurationPropertiesBean.java

/** * Provides access to {@link ConfigurationProperties @ConfigurationProperties} bean * details, regardless of if the annotation was used directly or on a {@link Bean @Bean} * factory method. This class can be used to access {@link #getAll(ApplicationContext) * all} configuration properties beans in an ApplicationContext, or * {@link #get(ApplicationContext, Object, String) individual beans} on a case-by-case * basis (for example, in a {@link BeanPostProcessor}). * * @author Phillip Webb * @since 2.2.0 * @see #getAll(ApplicationContext) * @see #get(ApplicationContext, Object, String) */public final class ConfigurationPropertiesBean {    private final String name;    private final Object instance;    private final ConfigurationProperties annotation;    private final Bindable<?> bindTarget;    private final BindMethod bindMethod;    //......}    
ConfigurationPropertiesBean用于代表一个标注了@ConfigurationProperties注解的bean的信息

ConfigurationPropertiesBinder

org/springframework/boot/context/properties/ConfigurationPropertiesBinder.java

/** * Internal class used by the {@link ConfigurationPropertiesBindingPostProcessor} to * handle the actual {@link ConfigurationProperties @ConfigurationProperties} binding. * * @author Stephane Nicoll * @author Phillip Webb */class ConfigurationPropertiesBinder {    private static final String BEAN_NAME = "org.springframework.boot.context.internalConfigurationPropertiesBinder";    private static final String FACTORY_BEAN_NAME = "org.springframework.boot.context.internalConfigurationPropertiesBinderFactory";    private static final String VALIDATOR_BEAN_NAME = EnableConfigurationProperties.VALIDATOR_BEAN_NAME;    private final ApplicationContext applicationContext;    private final PropertySources propertySources;    private final Validator configurationPropertiesValidator;    private final boolean jsr303Present;    private volatile Validator jsr303Validator;    private volatile Binder binder;    ConfigurationPropertiesBinder(ApplicationContext applicationContext) {        this.applicationContext = applicationContext;        this.propertySources = new PropertySourcesDeducer(applicationContext).getPropertySources();        this.configurationPropertiesValidator = getConfigurationPropertiesValidator(applicationContext);        this.jsr303Present = ConfigurationPropertiesJsr303Validator.isJsr303Present(applicationContext);    }    BindResult<?> bind(ConfigurationPropertiesBean propertiesBean) {        Bindable<?> target = propertiesBean.asBindTarget();        ConfigurationProperties annotation = propertiesBean.getAnnotation();        BindHandler bindHandler = getBindHandler(target, annotation);        return getBinder().bind(annotation.prefix(), target, bindHandler);    }    private Binder getBinder() {        if (this.binder == null) {            this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),                    getConversionService(), getPropertyEditorInitializer(), null,                    ConfigurationPropertiesBindConstructorProvider.INSTANCE);        }        return this.binder;    }    //......}    
ConfigurationPropertiesBinder的bind办法依据ConfigurationPropertiesBean的target与annotation取获取bindHandler,而后通过binder去执行bind办法
binder的结构器依赖了propertySources、placeholdersResolver、conversionService、propertyEditorInitializer、defaultBindHandler、constructorProvider

Binder

org/springframework/boot/context/properties/bind/Binder.java

    private <T> Object bindObject(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,            Context context, boolean allowRecursiveBinding) {        ConfigurationProperty property = findProperty(name, 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);    }    private AggregateBinder<?> getAggregateBinder(Bindable<?> target, Context context) {        Class<?> resolvedType = target.getType().resolve(Object.class);        if (Map.class.isAssignableFrom(resolvedType)) {            return new MapBinder(context);        }        if (Collection.class.isAssignableFrom(resolvedType)) {            return new CollectionBinder(context);        }        if (target.getType().isArray()) {            return new ArrayBinder(context);        }        return null;    }    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 <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;    }
bindObject办法先通过findProperty获取ConfigurationProperty,而后执行bindAggregate或者bindProperty;AggregateBinder次要是解决Map、Collection、Array类型;bindProperty办法这里从property获取绑定的值,而后resolvePlaceholders,最初通过converter的convert办法把值绑定到target上

BindConverter

org/springframework/boot/context/properties/bind/BindConverter.java

    <T> T convert(Object value, ResolvableType type, Annotation... annotations) {        if (value == null) {            return null;        }        return (T) this.conversionService.convert(value, TypeDescriptor.forObject(value),                new ResolvableTypeDescriptor(type, annotations));    }
BindConverter的convert办法则是通过conversionService进行

小结

ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四个接口;其getOrder办法返回的是Ordered.HIGHEST_PRECEDENCE + 1即仅次于最高的优先级;其postProcessBeforeInitialization办法次要是执行bind办法(先通过ConfigurationPropertiesBean.get获取ConfigurationPropertiesBean,再通过binder进行bind);其afterPropertiesSet次要是获取BeanDefinitionRegistry与ConfigurationPropertiesBinder