springboot@ConfigurationProperties注解源码解析(含松散绑定)springboot @ConfigurationProperties注解源码解析(含松散绑定)
⽂章⽬录
1.概要
属性绑定实现逻辑:
由springboot编写的org.t.properties.bind.Binder⼯具类实现,中间过程涉及多层递归,建议debug调试跟踪查看。
@ConfigurationProperties⾃动绑定实现逻辑:
由org.t.properties.ConfigurationPropertiesBindingPostProcessor BeanPostProcessor后置处理器实现。
松散绑定的实现逻辑:
①通过反射根据bean对象中的⽅法特别是setter,获取属性名称(统⼀为短横线形式),详
见:org.t.properties.bind.DataObjectPropertyName#toDashedForm
②根据@ConfigurationProperties注解prefix匹配属性组(其数据结构为Map<ConfigurationPropertyName, Set<String>>)。
③根据属性名称匹配属性组中的值,注意ConfigurationPropertyName重写了hashcode和equals,驼峰、下划线、短横线格式互认。
2.测试代码
⽰例代码1
@Test
public void test(){
final MockEnvironment environment =new MockEnvironment();
environment.setProperty("rocketmq.name-server","localhost:9876");
RocketMQProperties rocketMQProperties = (environment).bindOrCreate("rocketmq", Rock
etMQProperties.class);
System.out.NameServer());
}
⽰例代码2
@Test
public void test(){
final Map<String, Object> map =new HashMap<>(16);
map.putIfAbsent("rocketmq.nameServer","localhost:9876");
final Properties properties =new Properties();
properties.putIfAbsent("rocketmq.nameServer","localhost:9876");
final ConfigurableEnvironment environment =new StandardEnvironment();
final MutablePropertySources propertySources = PropertySources();
// 增加map属性组
propertySources.addLast(new MapPropertySource("messageQueueProperties", map));
// 增加properties属性组
// propertySources.addLast(new PropertiesPropertySource("messageQueueProperties", properties));
RocketMQProperties rocketMQProperties = (environment).bindOrCreate("rocketmq", RocketMQProperties.class);
System.out.NameServer());
}
3.调⽤链
由于以下调⽤过程存在递归调⽤存在,源代码分析过程⽐较乱,建议直接debug调试阅读源码,以下
内容仅供参考!
org.t.properties.ConfigurationProperties @ConfigurationProperties注解
=>
org.t.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization
BeanPostProcessor bean后置处理器
=>
org.t.properties.ConfigurationPropertiesBinder#bind 读取@ConfigurationProperties注解,将前缀作为ConfigurationPropertiesBean对象的bind name
=>
org.t.properties.bind.Binder#bind(java.lang.String, org.t.properties.bind.Bindable<T>, org.t.properties.bind.BindHandler)
=>
org.t.properties.bind.Binder#bindDataObject
=>
org.t.properties.bind.JavaBeanBinder.Bean#get 通过反射实例化⽬标类
……递归调⽤
=>
org.t.properties.bind.Binder#bindObject
=>
org.t.properties.source.SpringIterableConfigurationPropertySource#getConfigurationProperty
=>
org.t.properties.source.SpringIterableConfigurationPropertySource.Mappings#getMapped
重点:特别注意此处,从map对象中匹配属性值时,驼峰、下划线、短横线均可以取到。(原因是源码中重写
了org.t.properties.source.ConfigurationPropertyName类的hashcode和equals⽅法)。
4.@ConfigurationProperties注解处理
完整路径: org.t.properties.ConfigurationPropertiesBinder#bind
读取@ConfigurationProperties注解,将前缀作为ConfigurationPropertiesBean对象的bind name
BindResult<?>bind(ConfigurationPropertiesBean propertiesBean){
Bindable<?> target = propertiesBean.asBindTarget();
ConfigurationProperties annotation = Annotation();
BindHandler bindHandler =getBindHandler(target, annotation);
return getBinder().bind(annotation.prefix(), target, bindHandler);
}
5.属性绑定
完整路径: org.t.properties.bind.Binder
⽤途:将Spring容器Environment中的变量绑定到指定对象(除了⽀持String、Integer、Enum同样⽀持Map、List)。
有了此⼯具类,不使⽤@ConfigurationProperties注解也可以使⽤松散绑定的特性,享受springboot带来的便利!
org.t.properties.bind.Binder#bindObject
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&&Sources(), 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);
}
org.t.properties.bind.JavaBeanBinder源码
/
*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* /licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.t.properties.bind;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import flect.Field;
import flect.Method;
import flect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.beans.BeanUtils;
import org.t.properties.bind.Binder.Context;
import org.t.properties.source.ConfigurationPropertyName;
import org.t.properties.source.ConfigurationPropertySource;
import org.t.properties.source.ConfigurationPropertyState;
import MethodParameter;
import ResolvableType;
import ResolvableType;
/**
* {@link DataObjectBinder} for mutable Java Beans.
*
* @author Phillip Webb
* @author Madhura Bhave
*/
class JavaBeanBinder implements DataObjectBinder {
static final JavaBeanBinder INSTANCE =new JavaBeanBinder();
@Override
public<T> T bind(ConfigurationPropertyName name, Bindable<T> target, Context context,
DataObjectPropertyBinder propertyBinder){
boolean hasKnownBindableProperties = Value()!= null &&hasKnownBindableProperties(name, context); Bean<T> bean = (target, hasKnownBindableProperties);
if(bean == null){
return null;
}
BeanSupplier<T> beanSupplier = Supplier(target);
boolean bound =bind(propertyBinder, bean, beanSupplier, context);
return(bound ? (): null);
}
@Override
@SuppressWarnings("unchecked")
public<T> T create(Bindable<T> target, Context context){
Class<T> type =(Class<T>) Type().resolve();
return(type != null)? BeanUtils.instantiateClass(type): null;
}
private boolean hasKnownBindableProperties(ConfigurationPropertyName name, Context context){
for(ConfigurationPropertySource source : Sources()){
ainsDescendantOf(name)== ConfigurationPropertyState.PRESENT){
return true;
}
}
return false;
}
private<T>boolean bind(DataObjectPropertyBinder propertyBinder, Bean<T> bean, BeanSupplier<T> beanSupplier, Context context){
boolean bound =false;
for(BeanProperty beanProperty : Properties().values()){
bound |=bind(beanSupplier, propertyBinder, beanProperty);
context.clearConfigurationProperty();
}
return bound;
}
private<T>boolean bind(BeanSupplier<T> beanSupplier, DataObjectPropertyBinder propertyBinder,
BeanProperty property){
String propertyName = Name();
ResolvableType type = Type();
Supplier<Object> value = Value(beanSupplier);
Annotation[] annotations = Annotations();
Object bound = propertyBinder.bindProperty(propertyName,
Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
if(bound == null){springboot结构
return false;
}
if(property.isSettable()){
property.setValue(beanSupplier, bound);
}
else if(value == null ||!bound.())){
throw new IllegalStateException("No setter found for property: "+ Name());
throw new IllegalStateException("No setter found for property: "+ Name());
}
return true;
}
/**
* The bean being bound.
*
* @param <T> the bean type
*/
static class Bean<T>{
private static Bean<?> cached;
private final ResolvableType type;
private final Class<?> resolvedType;
private final Map<String, BeanProperty> properties =new LinkedHashMap<>();
Bean(ResolvableType type, Class<?> resolvedType){
addProperties(resolvedType);
}
private void addProperties(Class<?> type){
while(type != null &&!Object.class.equals(type)){
Method[] declaredMethods =getSorted(type, Class::getDeclaredMethods, Method::getName);
Field[] declaredFields =getSorted(type, Class::getDeclaredFields, Field::getName);
addProperties(declaredMethods, declaredFields);
type = Superclass();
}
}
private<S, E> E[]getSorted(S source, Function<S, E[]> elements, Function<E, String> name){
E[] result = elements.apply(source);
Arrays.sort(result, Comparatorparing(name));
return result;
}
protected void addProperties(Method[] declaredMethods, Field[] declaredFields){
for(int i =0; i < declaredMethods.length; i++){
if(!isCandidate(declaredMethods[i])){
declaredMethods[i]= null;
}
}
for(Method method : declaredMethods){
addMethodIfPossible(method,"get",0, BeanProperty::addGetter);
addMethodIfPossible(method,"is",0, BeanProperty::addGetter);
}
for(Method method : declaredMethods){
addMethodIfPossible(method,"set",1, BeanProperty::addSetter);
}
for(Field field : declaredFields){
addField(field);
}
}
private boolean isCandidate(Method method){
int modifiers = Modifiers();
return!Modifier.isPrivate(modifiers)&&!Modifier.isProtected(modifiers)&&!Modifier.isAbstract(modifiers) &&!Modifier.isStatic(modifiers)&&!Object.class.DeclaringClass())
&&!Class.class.DeclaringClass())&& Name().indexOf('$')==-1;
}
private void addMethodIfPossible(Method method, String prefix,int parameterCount,
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论