概要:
springboot獲取配置資源,主要分3種方式:@Value、 @ConfigurationProperties、Enviroment對象直接調(diào)用。
前2種底層實現(xiàn)原理,都是通過第三種方式實現(xiàn)。@Value 是spring原生功能,通過PropertyPlaceholderHelper.replacePlaceholders()方法,支持EL表達式的替換。
@ConfigurationProperties則是springboot 通過自動配置實現(xiàn),并且最后通過JavaBeanBinder 來實現(xiàn)松綁定
獲取資源的方式
1.1、@Value標(biāo)簽獲取
@Component
@Setter@Getter
public class SysValue {
@Value("${sys.defaultPW}")
private String defaultPW;
}
1.2、@ConfigurationProperties標(biāo)簽獲取
@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//初始化調(diào)用
public void init(){
defaultPW=environment.getProperty("sys.defaultPW");
}
}
PropertyResolver初始化與方法。
2.1、api解釋:
Interface for resolving properties against any underlying source.
(解析properties針對于任何底層資源的接口)
2.2、常用實現(xiàn)類
PropertySourcesPropertyResolver:配置源解析器。
Environment:environment對象也繼承了解析器。
2.3、常用方法。
java.lang.String getProperty(java.lang.String key):根絕 Key獲取值。
java.lang.String resolvePlaceholders(java.lang.String text)
:替換$(....)占位符,并賦予值。(@Value 底層通過該方法實現(xiàn))。
2.4、springboot中environment初始化過程初始化PropertySourcesPropertyResolver代碼。
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
//初始化environment抽象類是,會初始化PropertySourcesPropertyResolver,
//并將propertySources傳入。
//獲取邏輯猜想:propertySources是一個List<>。
//getProperty方法會遍歷List根據(jù)key獲取到value
//一旦獲取到value則跳出循環(huán),從而實現(xiàn)優(yōu)先級問題。
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:(根據(jù)key獲取value。)
Enviroment 對象源碼解析。
同上第三步,直接通過PropertySourcesPropertyResolver獲取值。
2.4也能發(fā)現(xiàn)Enviroment new的PropertyResolver是PropertySourcesPropertyResolver
@ConfigurationProperties實現(xiàn)原理
核心類:
ConfigurationPropertiesBindingPostProcessor
//通過自動配置,@EnableConfigurationProperties注入
//ConfigurationPropertiesBindingPostProcessor
@Configuration
@EnableConfigurationProperties
public class ConfigurationPropertiesAutoConfiguration {
}
ConfigurationPropertiesBindingPostProcessor 類解析
//綁定數(shù)據(jù)
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);
}
}
//調(diào)用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);
//調(diào)用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 來綁定數(shù)據(jù)
//為何ConfigurationProperties無法綁定靜態(tài)對象:
//JavaBeanBinder會過濾掉靜態(tài)方法
private boolean isCandidate(Method method) {
int modifiers = method.getModifiers();
return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers)
&& !Modifier.isStatic(modifiers)//非靜態(tài)方法
&& !Object.class.equals(method.getDeclaringClass())
&& !Class.class.equals(method.getDeclaringClass());
}
參考
[https://blog.csdn.net/andy_zhang2007/article/details/86355259]