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實現(xiàn)了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四個接口;其getOrder方法返回的是Ordered.HIGHEST_PRECEDENCE + 1即僅次于最高的優(yōu)先級;其postProcessBeforeInitialization方法主要是執(zhí)行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,最后創(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用于代表一個標(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,然后通過binder去執(zhí)行bind方法
binder的構(gòu)造器依賴了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,然后執(zhí)行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進行
小結(jié)
ConfigurationPropertiesBindingPostProcessor實現(xiàn)了BeanPostProcessor、PriorityOrdered、ApplicationContextAware、InitializingBean四個接口;
其getOrder方法返回的是Ordered.HIGHEST_PRECEDENCE + 1
即僅次于最高的優(yōu)先級;
其postProcessBeforeInitialization方法主要是執(zhí)行bind方法(先通過ConfigurationPropertiesBean.get獲取ConfigurationPropertiesBean,再通過binder進行bind
);
其afterPropertiesSet主要是獲取BeanDefinitionRegistry與ConfigurationPropertiesBinder
以上就是springboot ConfigurationProperties的綁定源碼示例解析的詳細內(nèi)容,更多關(guān)于springboot ConfigurationProperties綁定的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java創(chuàng)建txt文件并寫入內(nèi)容的方法代碼示例
這篇文章主要介紹了java創(chuàng)建txt文件并寫入內(nèi)容的兩種方法,分別是使用java.io.FileWriter和BufferedWriter,以及使用Java7的java.nio.file包中的Files和Path類,需要的朋友可以參考下2025-01-01一文帶你掌握J(rèn)ava?Future模式的靈活應(yīng)用
Future模式,簡單來說,就是一種能夠管理異步操作的方式,它可以讓咱們的程序在執(zhí)行一個耗時任務(wù)的同時,還能繼續(xù)做其他事情,下面我們就來看看Future模式的具體應(yīng)用吧2024-01-01