• 聊聊springboot的ConfigurationProperties的绑定


    本文主要研究一下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);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    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 bindTarget = Bindable.of(bindType).withAnnotations(annotations);
    		if (instance != null) {
    			bindTarget = bindTarget.withExistingValue(instance);
    		}
    		return new ConfigurationPropertiesBean(name, instance, annotation, bindTarget);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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;
    
    	//......
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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;
    	}
    
    	//......
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    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  Object bindObject(ConfigurationPropertyName name, Bindable 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  Object bindAggregate(ConfigurationPropertyName name, Bindable 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 bindProperty(Bindable 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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    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 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));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    BindConverter的convert方法则是通过conversionService进行

    小结

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

  • 相关阅读:
    [附源码]JAVA毕业设计个人饮食营养管理信息系统(系统+LW)
    【C++深入浅出】C/C++内存管理(教你如何new到对象)
    『华强买瓜』奇袭好莱坞!Jupyter也能创建可交互仪表板啦!超全面的英语论文写作套路;神经辐射场NeRF工具包;前沿论文 | ShowMeAI资讯日报
    cas244193-52-0|[C8MIm]BF4|1-辛基-3-甲基咪唑四氟硼酸盐离子液体分子式:C12H23BF4N2
    深度学习【QA语料库准备、文本分词、分类目的和方法、使用fastText实现文本分类】
    Android音乐播放器(三)轮播图
    【愚公系列】华为云系列之ModelArts+AppCube带你识别101种西式美食【我的低代码AI体验】
    [机缘参悟-39]:鬼谷子-第五飞箝篇 - 警示之二:赞美的六种类型,谨防享受赞美快感如同鱼儿享受诱饵。
    第七章 排序
    力扣第200题 岛屿数量 C++ dfs bfs 深搜和广搜 附Java代码
  • 原文地址:https://blog.csdn.net/hello_ejb3/article/details/132730375