获取属性源码跟踪解析
入口方法
@Component
public class EnvironmentDebugger implements CommandLineRunner, EnvironmentAware {
private Environment environment;
@Override
public void run(String... args) throws Exception {
String study2 = environment.getProperty("study2");
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
实现EnvironmentAware 接口,获取Environment对象
实现CommandLineRunner接口,在容器启动完成后,获取key的值,作为源码跟踪的入口
源码跟踪
根据入口environment.getProperty,一直点进入org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class, boolean),可以看到最基础的实现
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}
方法中可以看出通过key获取属性值,遍历propertySources中存放的PropertySource,并且从中依次取出属性,如果所有都没有取到,则返回NULL
可以看出propertySources中共存在7中类型的属性,可以找到我们设置的默认属性,并且已键值对存放数据
PropertySource源码
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
}
source为存放属性的具体实现
通过获取属性可以看出所有的属性都存放在:propertySources中,只需要搞懂spring容器在启动的时候是如何装载自定义的属性进propertySources就可以
Environment加载属性
入口函数
springApplication.run(args) --> prepareEnvironment
prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
在prepareEnvironment中,共进行了一下的进步操作:
- 创建Environment对象:getOrCreateEnvironment()
- 配置属性:configureEnvironment()
- 添加configurationProperties属性:ConfigurationPropertySources.attach(environment)
- 通过事件发布器发布environment事件:listeners.environmentPrepared(bootstrapContext, environment);
- 将defaultProperties属性移到最后面,因为是默认,所有在后面兜底:DefaultPropertiesPropertySource.moveToEnd(environment)
- 加载profile配置文件属性:configureAdditionalProfiles(environment)
- 绑定SpringApplication对象到Environment中:bindToSpringApplication(environment)
getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
- 通过:webApplicationType判断当前容器环境,创建不同的Environment对象
SERVLET环境举例
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
}
可以看出在类种定义了方法:customizePropertySources,
customizePropertySources:
- 添加:servletContextInitParams属性集
- 添加:servletConfigInitParams属性集
- 如果jndi环境,添加jndiProperties属性集
- 调用付类customizePropertySources方法,初始化java及系统属性参数
supper.customizePropertySources()
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
在StandardServletEnvironment类中,并没有发现在哪里调用:customizePropertySources().此时查看类图,它的的上层父类:StandardEnvironment也没有调用,继续查看上层父类:AbstractEnvironment,此时发现在构造函数中调用了customizePropertySources()
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
只是由于java在实例化的时候,会先调用父类的构造函数,所在执行了customizePropertySources(),完成相关属性的初始化工作
- 此时environment中的属性只有四种
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 添加解析器
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
在方法中通过:configurePropertySources方法来添加:
- 添加默认属性值
- 添加命令行属性值
configurePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
1.通过函数:DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast) 将硬编码的defaultProperties对象属性添加在sources中
public static void main(String[] args) throws InterruptedException {
SpringApplication springApplication = new SpringApplication((SpringBootDemoApplication.class));
Properties properties = new Properties();
properties.setProperty("study1", "value_study1");
springApplication.setDefaultProperties(properties);
springApplication.run(args);
}
//springApplication.setDefaultProperties(properties)
public void setDefaultProperties(Properties defaultProperties) {
this.defaultProperties = new HashMap<>();
for (Object key : Collections.list(defaultProperties.propertyNames())) {
this.defaultProperties.put((String) key, defaultProperties.get(key));
}
}
2.判断是否需要添加命令行参数及args是否传入的信息,构建一个SimpleCommandLinePropertySource对象添加到属性列表中
new SimpleCommandLinePropertySource(args)
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
调用SimpleCommandLineArgsParser解析器,解析传入的参数信息
SimpleCommandLineArgsParser.parse
class SimpleCommandLineArgsParser {
/**
* Parse the given {@code String} array based on the rules described {@linkplain
* SimpleCommandLineArgsParser above}, returning a fully-populated
* {@link CommandLineArgs} object.
* @param args command line arguments, typically from a {@code main()} method
*/
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2);
String optionName;
String optionValue = null;
int indexOfEqualsSign = optionText.indexOf('=');
if (indexOfEqualsSign > -1) {
optionName = optionText.substring(0, indexOfEqualsSign);
optionValue = optionText.substring(indexOfEqualsSign + 1);
}
else {
optionName = optionText;
}
if (optionName.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
}
可以看出只对-- 开头的字符串进行解析,根据键值对来获取key,value,添加到属性中
- 此时environment中的属性值
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment、commandLineArgs、defaultProperties
ConfigurationPropertySources.attach
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
主要设置configurationProperties属性值
- 此时environment属性值
servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment、commandLineArgs、defaultProperties、 configurationProperties
listeners.environmentPrepared
DefaultPropertiesPropertySource.moveToEnd
public static final String NAME = "defaultProperties";
public static void moveToEnd(MutablePropertySources propertySources) {
PropertySource<?> propertySource = propertySources.remove(NAME);
if (propertySource != null) {
propertySources.addLast(propertySource);
}
}
先将defaultProperties删除,然后在添加,目的是将defaultProperties存到最后一个节点,用作兜底操作
configureAdditionalProfiles
private void configureAdditionalProfiles(ConfigurableEnvironment environment) {
if (!CollectionUtils.isEmpty(this.additionalProfiles)) {
Set<String> profiles = new LinkedHashSet<>(Arrays.asList(environment.getActiveProfiles()));
if (!profiles.containsAll(this.additionalProfiles)) {
profiles.addAll(this.additionalProfiles);
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
}
}
获取当前需要加载的配置文件后缀
bindToSpringApplication
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
将spring.main属性值赋值给:SpringApplication中对应的属性
ConfigurationClassParser
- 添加@PropertySources属性集,在run的refresh中调用
org.springframework.context.annotation.ConfigurationClassParser#processPropertySource
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
propertySources.addLast(propertySource);
}
else {
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}