@ConfigurationProperties源码分析 一

@ConfigurationProperties源码分析 一

@ConfigurationProperties源码分析 二 什么时候注册的ConfigurationPropertiesBindingPostProcessor?
@ConfigurationProperties源码分析 三 什么时候注册的ConfigurationClassPostProcessor?
@ConfigurationProperties源码分析 四 什么时候注册的ConfigurationPropertiesBindingPostProcessorRegistrar?
@ConfigurationProperties源码分析 五 如何实现递归赋值配置类成员变量?

背景

之前我们项目当中,配置文件是通过 @Value("${zk.register.merinfo}") 方式注入的。
造成问题如下:

  • 配置属性不统一,没有结构。
  • 注入麻烦每个属性都要写配置名,和属性名。(只要有重复的工作,就应该重构)
  • 配置零散在项目中各处
@Component
public class ApiBaseInfoService {

	@Value("${zk.register.merinfo}")
	public String merInfoRpc;
	
	@Value("${pay.qpass.trade}")
	public String payQpassTrade;
	@Value("${pay.qpass.refund}")
	public String payQpassRefund;
	@Value("${pay.qpass.reverse}")
	public String payQpassReverse;
	
}

修改后我们的代码如下.


@Configuration
@ConfigurationProperties(prefix = "jlpay")
@Data
public class TestConf {
    private  Notify notify = new Notify();
    private  String node;

    public static TestConf TestConfStatic ;
    {
        TestConfStatic =this;
    }
    @Data
    public   class   Notify {
        String risk;
        String  trans;
    }
}
jlpay:
  notify:
    risk: risk-url
    trans: trans-url
  node: node-url

问题

那么就是是如何通过@ConfigurationProperties 将配置文件中的值赋值到配置类里面的呢?
猜想如下:

既然有注解,那肯定有注解处理器。
在配置类初始化的时候调用注解处理器处理
注解处理器会读取@ConfigurationProperties 注解的对象
获取配置文件中的prefix,和注解对象的类成员变量
递归将配置属性赋值给类成员变量

源码分析

ConfigurationPropertiesBindingPostProcessor

Spring 初始化Bean 的时候会调用 bean后置处理 ConfigurationPropertiesBindingPostProcessor 的postProcessBeforeInitialization方法
这里其实就是我们上面猜想的注解处理器,它会处理ConfigurationProperties注解
获取配置文件中的prefix,和注解对象的类成员变量

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
		throws BeansException {
			//获取@ConfigurationProperties注解信息
			ConfigurationProperties annotation = getAnnotation(bean, beanName,
			ConfigurationProperties.class);
			//有注解的才绑定配置属性 没有的直接返回
			if (annotation != null) {
				bind(bean, beanName, annotation);
			}
			return bean;
	}
	private void bind(Object bean, String beanName, ConfigurationProperties annotation) {
		ResolvableType type = getBeanType(bean, beanName);
		Validated validated = getAnnotation(bean, beanName, Validated.class);
		Annotation[] annotations = (validated != null)
				? new Annotation[] { annotation, validated }
				: new Annotation[] { annotation };
				//将bean 类型 注解等信息包装传递给后面处理
		Bindable<?> target = Bindable.of(type).withExistingValue(bean)
				.withAnnotations(annotations);
		try {
		//委托配置绑定器来处理
			this.configurationPropertiesBinder.bind(target);
		}
		catch (Exception ex) {
			throw new ConfigurationPropertiesBindException(beanName, bean, annotation,
					ex);
		}
	}
ConfigurationPropertiesBinder

上面的注解处理器委托配置绑定器ConfigurationPropertiesBinder来处理 ,主要考虑职责分离。

	public void bind(Bindable<?> target) {
		ConfigurationProperties annotation = target
				.getAnnotation(ConfigurationProperties.class);
		Assert.state(annotation != null,
				() -> "Missing @ConfigurationProperties on " + target);
		List<Validator> validators = getValidators(target);
		BindHandler bindHandler = getBindHandler(annotation, validators);
		//委托Binder处理
		getBinder().bind(annotation.prefix(), target, bindHandler);
	}
Binder

委托Binder处理

	private Binder getBinder() {
		if (this.binder == null) {
			this.binder = new Binder(getConfigurationPropertySources(),
			getPropertySourcesPlaceholdersResolver(), getConversionService(),
			getPropertyEditorInitializer());
		}
		return this.binder;
	}
private Object bindBean(ConfigurationPropertyName name, Bindable<?> target,
			BindHandler handler, Context context, boolean allowRecursiveBinding) {
		if (containsNoDescendantOf(context.streamSources(), name)
				|| isUnbindableBean(name, target, context)) {
			return null;
		}
		//用于递归绑定bean属性的绑定器。 理解成回调函数,后面会递归调用给bean属性赋值
		BeanPropertyBinder propertyBinder = (propertyName, propertyTarget) -> bind(
				name.append(propertyName), propertyTarget, handler, context, false);
		Class<?> type = target.getType().resolve(Object.class);
		if (!allowRecursiveBinding && context.hasBoundBean(type)) {
			return null;
		}
		//用了java8 lambda 表达式 和流操作
		return context.withBean(type, () -> {
			Stream<?> boundBeans = BEAN_BINDERS.stream()  // BEAN_BINDERS对象 实际是 JavaBeanBinder List
					.map((b) -> b.bind(name, target, context, propertyBinder));
			return boundBeans.filter(Objects::nonNull).findFirst().orElse(null);
		});
	}

JavaBeanBinder

上面的BEAN_BINDERS对象 实际是 JavaBeanBinder List

	private static final List<BeanBinder> BEAN_BINDERS;
	static {
		List<BeanBinder> binders = new ArrayList<>();
		binders.add(new JavaBeanBinder());
		BEAN_BINDERS = Collections.unmodifiableList(binders);
	}

遍历属性赋值

	private <T> boolean bind(BeanPropertyBinder propertyBinder, Bean<T> bean,
			BeanSupplier<T> beanSupplier) {
		boolean bound = false;
		for (Map.Entry<String, BeanProperty> entry : bean.getProperties().entrySet()) {
			bound |= bind(beanSupplier, propertyBinder, entry.getValue());
		}
		return bound;
	}

递归调用

	private <T> boolean bind(BeanSupplier<T> beanSupplier,
			BeanPropertyBinder propertyBinder, BeanProperty property) {
		String propertyName = property.getName();
		ResolvableType type = property.getType();
		Supplier<Object> value = property.getValue(beanSupplier);
		Annotation[] annotations = property.getAnnotations();
		//这里就是上面提到的回调函数,会递归对象属性赋值
		Object bound = propertyBinder.bindProperty(propertyName,
				Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
		if (bound == null) {
			return false;
		}
		//判断有setter方法,调用赋值,用的是java 反射技术
		if (property.isSettable()) {
			property.setValue(beanSupplier, bound);
		}
		else if (value == null || !bound.equals(value.get())) {
			throw new IllegalStateException(
					"No setter found for property: " + property.getName());
		}
		return true;
	}

主要就是这里实现赋值的 property.setValue(beanSupplier, bound);用的是java 反射技术

		public void setValue(Supplier<?> instance, Object value) {
			try {
				this.setter.setAccessible(true);
				this.setter.invoke(instance.get(), value);
			}
			catch (Exception ex) {
				throw new IllegalStateException(
						"Unable to set value for property " + this.name, ex);
			}
		}

总结

究竟是如何通过@ConfigurationProperties 将配置文件中的值赋值到配置类里面的呢?

注册了ConfigurationProperties 注解处理器ConfigurationPropertiesBindingPostProcessor,它实现了Spring容器的postProcessBeforeInitialization方法,会在bean初始化之前被调用
注解处理器会读取@ConfigurationProperties 注解的对象
获取配置文件中的prefix,和注解对象的类成员变量
然后递归将配置属性赋值给类成员变量

链接

其他问题

github

完整代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值