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);
}
}ConfigurationPropertiesBindingPostProcessor實(shí)現(xiàn)了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四個(gè)接口;其getOrder方法返回的是Ordered.HIGHEST_PRECEDENCE + 1即僅次于最高的優(yōu)先級(jí);其postProcessBeforeInitialization方法主要是執(zhí)行bind方法(先通過(guò)ConfigurationPropertiesBean.get獲取ConfigurationPropertiesBean,再通過(guò)binder進(jìn)行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,最后創(chuàng)建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用于代表一個(gè)標(biāo)注了@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方法根據(jù)ConfigurationPropertiesBean的target與annotation取獲取bindHandler,然后通過(guò)binder去執(zhí)行bind方法
binder的構(gòu)造器依賴(lài)了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方法先通過(guò)findProperty獲取ConfigurationProperty,然后執(zhí)行bindAggregate或者bindProperty;AggregateBinder主要是處理Map、Collection、Array類(lèi)型;bindProperty方法這里從property獲取綁定的值,然后resolvePlaceholders,最后通過(guò)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方法則是通過(guò)conversionService進(jìn)行
小結(jié)
ConfigurationPropertiesBindingPostProcessor實(shí)現(xiàn)了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四個(gè)接口;
其getOrder方法返回的是Ordered.HIGHEST_PRECEDENCE + 1即僅次于最高的優(yōu)先級(jí);
其postProcessBeforeInitialization方法主要是執(zhí)行bind方法(先通過(guò)ConfigurationPropertiesBean.get獲取ConfigurationPropertiesBean,再通過(guò)binder進(jìn)行bind);
其afterPropertiesSet主要是獲取BeanDefinitionRegistry與ConfigurationPropertiesBinder
以上就是springboot ConfigurationProperties的綁定源碼示例解析的詳細(xì)內(nèi)容,更多關(guān)于springboot ConfigurationProperties綁定的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java創(chuàng)建txt文件并寫(xiě)入內(nèi)容的方法代碼示例
這篇文章主要介紹了java創(chuàng)建txt文件并寫(xiě)入內(nèi)容的兩種方法,分別是使用java.io.FileWriter和BufferedWriter,以及使用Java7的java.nio.file包中的Files和Path類(lèi),需要的朋友可以參考下2025-01-01
Java stringBuilder的使用方法及實(shí)例解析
這篇文章主要介紹了Java stringBuilder的使用方法及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
SparkStreaming整合Kafka過(guò)程詳解
這篇文章主要介紹了SparkStreaming整合Kafka過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01
一文帶你掌握J(rèn)ava?Future模式的靈活應(yīng)用
Future模式,簡(jiǎn)單來(lái)說(shuō),就是一種能夠管理異步操作的方式,它可以讓咱們的程序在執(zhí)行一個(gè)耗時(shí)任務(wù)的同時(shí),還能繼續(xù)做其他事情,下面我們就來(lái)看看Future模式的具體應(yīng)用吧2024-01-01
SpringBoot接入釘釘自定義機(jī)器人預(yù)警通知
本文主要介紹了SpringBoot接入釘釘自定義機(jī)器人預(yù)警通知,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

