springboot获取配置文件enviroment.Properties的几种方式
springboot获取配置资源,主要分3种方式:@Value、 @ConfigurationProperties、Enviroment对象直接调用。
前2种底层实现原理,都是通过第三种方式实现。
@Value 是spring原生功能,通过PropertyPlaceholderHelper.replacePlaceholders()方法,支持EL表达式的替换。
@ConfigurationProperties则是springboot 通过自动配置实现,并且最后通过JavaBeanBinder 来实现松绑定
获取资源的方式
1.1、@Value标签获取
@Component @Setter@Getter public class SysValue { @Value("${sys.defaultPW}") private String defaultPW; }
1.2、@ConfigurationProperties标签获取
@Component @ConfigurationProperties(prefix = "sys") @Setter@Getter public class SysConfig { private String defaultPW; }
1.3、直接Enviroment对象获取回去
@Component public class EnvironmentValue { @Autowired Environment environment; private String defaultPW; @PostConstruct//初始化调用 public void init(){ defaultPW=environment.getProperty("sys.defaultPW"); } }
1.4、前面几种都是需要在spring初始化之后,如果想要在bean初始化之前就获取配置文件,则需要实现BeanPostProcessor
package com.sohu.tv.mq.cloud.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "spring.datasource") public class DatabaseConfigHelper implements BeanPostProcessor { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private String url; private String userName; private String password; private String driverClassName; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { logger.error("url is " + url); logger.error("username is " + userName); logger.error("passwd is " + password); logger.error("driverClassName is " + driverClassName); return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } }
PropertyResolver初始化与方法。
2.1、api解释:
Interface for resolving properties against any underlying source.
(解析properties针对于任何底层资源的接口)
2.2、常用实现类
PropertySourcesPropertyResolver:配置源解析器。
Environment:environment对象也继承了解析器。
2.3、常用方法。
java.lang.String getProperty(java.lang.String key):根绝 Key获取值。
java.lang.String resolvePlaceholders(java.lang.String text)
:替换$(....)占位符,并赋予值。(@Value 底层通过该方法实现)。
2.4、springboot中environment初始化过程初始化PropertySourcesPropertyResolver代码。
public abstract class AbstractEnvironment implements ConfigurableEnvironment { //初始化environment抽象类是,会初始化PropertySourcesPropertyResolver, //并将propertySources传入。 //获取逻辑猜想:propertySources是一个List<>。 //getProperty方法会遍历List根据key获取到value //一旦获取到value则跳出循环,从而实现优先级问题。 private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources); }
@Value获取资源源码分析。
解析过程涉及到(MMP看了一晚上看不懂,补贴代码了,贴个过程):
AutowiredAnnotationBeanPostProcessor:(@Value注解解析,赋值)
//赋值代码Autowired AnnotationBeanPostProcessor.AutowiredFieldElement.inject if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); }
PropertySourcesPlaceholderConfigurer:(通过配置资源替换表达式)
PropertySourcesPropertyResolver:(根据key获取value。)
Enviroment 对象源码解析。
同上第三步,直接通过PropertySourcesPropertyResolver获取值。
2.4也能发现Enviroment new的PropertyResolver是PropertySourcesPropertyResolver
@ConfigurationProperties实现原理
核心类:
ConfigurationPropertiesBindingPostProcessor
//通过自动配置,@EnableConfigurationProperties注入 //ConfigurationPropertiesBindingPostProcessor @Configuration @EnableConfigurationProperties public class ConfigurationPropertiesAutoConfiguration { }
ConfigurationPropertiesBindingPostProcessor 类解析
//绑定数据 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 }; 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 .bind方法。 class 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); //调用getBinder方法 getBinder().bind(annotation.prefix(), target, bindHandler); } //getBinder方法初始化Binder对象 // 传入熟悉的PropertySources:也来自PropertySourcesPlaceholderConfigurer对象同@Value //PropertySourcesPlaceholdersResolver private Binder getBinder() { if (this.binder == null) { this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(), getConversionService(), getPropertyEditorInitializer()); } return this.binder; } }
Binder.bind()方法解析
//很深,最后通过JavaBeanBinder 来绑定数据 //为何ConfigurationProperties无法绑定静态对象: //JavaBeanBinder会过滤掉静态方法 private boolean isCandidate(Method method) { int modifiers = method.getModifiers(); return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers) && !Modifier.isStatic(modifiers)//非静态方法 && !Object.class.equals(method.getDeclaringClass()) && !Class.class.equals(method.getDeclaringClass()); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?