SpringBoot中@ConfigurationProperties注解的使用与源码详解

 更新时间:2023年11月18日 09:42:21   作者:morris131  
这篇文章主要介绍了SpringBoot中@ConfigurationProperties注解的使用与源码详解,@ConfigurationProperties注解用于自动配置绑定,可以将application.properties配置中的值注入到bean对象上,需要的朋友可以参考下

前言

相信大家肯定了解@Value注解,它可以通过一个配置文件中的属性名与对象的属性进行绑定。

@ConfigurationProperties注解的作用其实就类似于使用多个@Value注解同时绑定一个对象的多个属性,@ConfigurationProperties注解用于自动配置绑定,可以将application.properties配置中的值(准确来说是Environment中的属性值)注入到bean对象上,该注解的使用必须先将对象注入到IOC容器中才有配置自动绑定的功能。

@ConfigurationProperties注解的使用

先来看下@ConfigurationProperties的源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface ConfigurationProperties {
	// 匹配的前缀
    @AliasFor("prefix")
    String value() default "";

	// 同上
    @AliasFor("value")
    String prefix() default "";

	// 忽略属性类型不匹配的字段
    boolean ignoreInvalidFields() default false;

	// 忽略类中未知的属性,ignoreUnknownFields=false后当出现未知字段时会出现异常
    boolean ignoreUnknownFields() default true;
}

注解使用在类上:

package com.morris.spring.boot.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "database")
public class DatabaseProperties {

	private String username;

	private String password;

	private String driverClass;

	private String connectionUrl;

}

配置文件中的属性名称需要与实体类的属性保持一致,不然值会绑定不上,多个单词可以使用横杠进行分割,SpringBoot会将横杠命名转驼峰命名。

注解使用方法上:

package com.morris.spring.boot.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

@Configuration
public class FlowRuleConfig {

	@Bean
	@ConfigurationProperties("flow.config")
	public RuleProperties ruleProperties() {
		return new RuleProperties();
	}

	@Data
	public static class RuleProperties {
		private Map<String, Integer> rules;
	}
}

@ConfigurationProperties注解的原理

在SpringBoot的spring.factories文件注入了org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration。

ConfigurationPropertiesAutoConfiguration

ConfigurationPropertiesAutoConfiguration上面加了@EnableConfigurationProperties注解。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {

}

@EnableConfigurationProperties

@EnableConfigurationProperties注解导入了EnableConfigurationPropertiesRegistrar类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {

	/**
	 * The bean name of the configuration properties validator.
	 * @since 2.2.0
	 */
	String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

	/**
	 * Convenient way to quickly register
	 * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
	 * Spring. Standard Spring Beans will also be scanned regardless of this value.
	 * @return {@code @ConfigurationProperties} annotated beans to register
	 */
	Class<?>[] value() default {};

}

EnableConfigurationPropertiesRegistrar

EnableConfigurationPropertiesRegistrar实现了ImportBeanDefinitionRegistrar接口,主要用于向Spring容器中注入Bean。

主要注入了以下Bean:

  • ConfigurationPropertiesBindingPostProcessor
  • BoundConfigurationProperties
  • MethodValidationExcludeFilter
  • ConfigurationPropertiesBinder

org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar#registerBeanDefinitions

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
	// 注入ConfigurationPropertiesBindingPostProcessor
	// 注入BoundConfigurationProperties
	registerInfrastructureBeans(registry);
	// 注入MethodValidationExcludeFilter
	registerMethodValidationExcludeFilter(registry);

	// 注入@EnableConfigurationProperties注解指定的ConfigurationProperties
	// 例如org.springframework.boot.autoconfigure.web.ServerProperties
	ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
	getTypes(metadata).forEach(beanRegistrar::register);
}

ConfigurationPropertiesBindingPostProcessor

ConfigurationPropertiesBindingPostProcessor实现了BeanPostProcessor接口,其postProcessBeforeInitialization()方法会在Bean实例化后执行,在这里完成了配置文件的属性与对象的属性的绑定。

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 {
		// 主要是通过ConfigurationPropertiesBinder来完成配置文件的属性与对象的属性的绑定
		// 最终会调用Binder这个类来完成绑定
		this.binder.bind(bean);
	}
	catch (Exception ex) {
		throw new ConfigurationPropertiesBindException(bean, ex);
	}
}

ConfigurationPropertiesBinder

ConfigurationPropertiesBinder主要负责构建Binder并进行缓存,SpringBoot启动过程中已经将配置文件的属性值存到Environment中的PropertySources中了,所以Binder只要从Environment中获取即可。

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(),
								 getConversionServices(), getPropertyEditorInitializer(), null,
								 ConfigurationPropertiesBindConstructorProvider.INSTANCE);
	}
	return this.binder;
}

如何动态刷新@ConfigurationProperties

如果配置中心配置更新了,遇到了@ConfigurationProperties标注的配置bean,那么bean的属性就不会自动更新了,那么实现动态更新@ConfigurationProperties标注的bean的属性呢?

如果使用的Nacos注册中心,可以监听NacosConfigReceivedEvent事件后使用SpringBoot提供的Binder进行bean的属性的更新:

DatabaseProperties databaseProperties = applicationContext.getBean(DatabaseProperties.class);
System.out.println(databaseProperties);

// test refresh @ConfigurationProperties
// 这里使用app.properties模拟
ClassPathResource classPathResource = new ClassPathResource("app.properties");
ResourcePropertySource resourcePropertySource = new ResourcePropertySource(classPathResource);
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(resourcePropertySource);
Binder binder = new Binder(configurationPropertySource);
Bindable<DatabaseProperties> bindable = Bindable.ofInstance(databaseProperties);
binder.bind("database", bindable);
System.out.println(databaseProperties);

当前最简单的办法就是在bean上面加上@RefreshScope注解就能实现自动刷新属性值了。

到此这篇关于SpringBoot中@ConfigurationProperties注解的使用与源码详解的文章就介绍到这了,更多相关@ConfigurationProperties注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java发送http请求调用第三方接口获取token方式

    Java发送http请求调用第三方接口获取token方式

    这篇文章主要介绍了Java发送http请求调用第三方接口获取token方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 详解SpringBoot异常处理流程及原理

    详解SpringBoot异常处理流程及原理

    今天给大家带来的是关于Java的相关知识,文章围绕着SpringBoot异常处理流程及原理展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • SpringBoot集成Druid实现多数据源的两种方式

    SpringBoot集成Druid实现多数据源的两种方式

    这篇文章主要介绍了SpringBoot集成Druid实现多数据源的两种方式,集成com.baomidou的方式和基于AOP手动实现多数据源原生的方式,文中通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-03-03
  • SpringBoot项目Jar包如何瘦身部署的实现

    SpringBoot项目Jar包如何瘦身部署的实现

    这篇文章主要介绍了SpringBoot项目Jar包如何瘦身部署的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • SpringBoot中自定义参数绑定步骤详解

    SpringBoot中自定义参数绑定步骤详解

    这篇文章主要介绍了SpringBoot中自定义参数绑定步骤详解,非常不错,具有参考借鉴价值 ,需要的朋友可以参考下
    2018-02-02
  • 关于Spring的AnnotationAwareAspectJAutoProxyCreator类解析

    关于Spring的AnnotationAwareAspectJAutoProxyCreator类解析

    这篇文章主要介绍了关于Spring的AnnotationAwareAspectJAutoProxyCreator类解析,Spring是一个开源免费的框架 , 容器,是一个轻量级的框架 ,需要的朋友可以参考下
    2023-05-05
  • Java结构型设计模式中代理模式示例详解

    Java结构型设计模式中代理模式示例详解

    代理模式(Proxy Parttern)为一个对象提供一个替身,来控制这个对象的访问,即通过代理对象来访问目标对象。本文将通过示例详细讲解一下这个模式,需要的可以参考一下
    2022-09-09
  • java编程简单获取图片像素的方法

    java编程简单获取图片像素的方法

    这篇文章主要介绍了java编程简单获取图片像素的方法,涉及Java针对图片的读取与属性获取技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • Java Web项目中使用Socket通信多线程、长连接的方法

    Java Web项目中使用Socket通信多线程、长连接的方法

    很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序启动时,启动socket监听。接下来通过本文给大家介绍Java Web项目中使用Socket通信多线程、长连接的方法,感兴趣的朋友一起学习
    2016-04-04
  • SpringCloud服务网关Gateway的使用教程详解

    SpringCloud服务网关Gateway的使用教程详解

    SpringCloud Gateway是Spring体系内的一个全新项目,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。本文就来为大家详细讲讲Gateway的使用教程,需要的可以参考一下
    2022-09-09

最新评论