本文讨论的 Spring 版本为 4.3.13-RELEASE

加载

PropertySourcesPlaceholderConfigurer

这个是当前Spring环境的所有 properties 的抽象.

可以看到它在 postProcessBeanFactory 时开始初始化整个 properties 输入源

img

每个输入源, 都保存在 org.springframework.core.env.MutablePropertySources对象中, 该对象内部使用 private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>(); 来维护.

它的源码如下

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if (this.propertySources == null) {
			this.propertySources = new MutablePropertySources();
			if (this.environment != null) {
				this.propertySources.addLast(
					new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
						@Override
						public String getProperty(String key) {
							return this.source.getProperty(key);
						}
					}
				);
			}
			try {
				PropertySource<?> localPropertySource =
						new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
				if (this.localOverride) {
					this.propertySources.addFirst(localPropertySource);
				}
				else {
					this.propertySources.addLast(localPropertySource);
				}
			}
			catch (IOException ex) {
				throw new BeanInitializationException("Could not load properties", ex);
			}
		}

		processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
		this.appliedPropertySources = this.propertySources;
	}

environmentProperties

  • application.properties
  • 在 application.properties 中通过 spring.profiles.include, spring.profiles.active 等方式的
  • 或在代码中使用 @PropertySource(value = {"classpath:hello.properties"}), @Configuration 注解的, 都属于 environmentProperties

environment 对象, 是由Spring框架基础设置进来的.

				this.propertySources.addLast(
					new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
						@Override
						public String getProperty(String key) {
							return this.source.getProperty(key);
						}
					}
				);

通过源码可以看到, 刚开始时, propertySources 是空的, 所以创建了一个 MutablePropertySources 对象. 然后将当前环境相关的 properties 添加到该对象中, 即:

img

localPropertySource

PropertySource<?> localPropertySource = new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());

它会根据 localPropertySource 优先级(通过 localOverride 来判断, 默认是 false) 来决定是添加到列表的开头还是结尾.

加载完成后, 就可以看到有如下的输入源了

img

触发 localPropertySource 的例子

package com.example.value.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class LocalPropertiesDemo {
    @Bean
    public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertyPlaceholderConfigurer.setLocation(new ClassPathResource("mylocal.properties"));
        return propertyPlaceholderConfigurer;
    }
}

img

优先级的例子

application.properties

local.value=from local application.properties

mylocal.properties

local.value=from local mylocal.properties

不设置 PropertySourcesPlaceholderConfigurersetLocalOverride(true) , 则结果:

curl http://localhost:8080/hello
from local application.properties

设置 setLocalOverride(true)

package com.example.value.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class LocalPropertiesDemo {
    @Bean
    public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
        propertyPlaceholderConfigurer.setLocation(new ClassPathResource("mylocal.properties"));
        propertyPlaceholderConfigurer.setLocalOverride(true);
        return propertyPlaceholderConfigurer;
    }
}

则结果:

curl http://localhost:8080/hello
from local mylocal.properties

总结

  • 默认情况下, environmentProperties 优先级大于 localPropertySource
  • 当设置了 setLocalOverride 为 true 时, 则 localPropertySource 优先级大于 environmentProperties

构建 PropertySourcesPropertyResolver

根据上面的加载完 propertySources 后, 就构建解析器, 最后进行相应的 Bean 处理了.

processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));

什么是 local properties

local properties 就是那些通过基于 PropertiesLoaderSupport 的API来手动或编程的方式配置的 properties . 例如 (setProperties, setLocations 等).

进行实际的注入

这就要回到 Spring 框架中 IoC 容器的处理了, 这里只是描述了一个简单的 @Value 是如何进行注入的, 其他的 @Autowired 等依赖注入是同理的.

Spring 框架中, 通过注解来进行注入的处理器, 是 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

通过名字, 也可以大概猜测它的意思了, 就是 Autowired 相关的注解处理器(默认情况下, 它也会处理 @Value).

实际处理的代码源码(源文件的 599 行开始)

	private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {

		private final boolean required;

		private volatile boolean cached = false;

		private volatile Object cachedFieldValue;

		public AutowiredFieldElement(Field field, boolean required) {
			super(field, null);
			this.required = required;
		}

		@Override
		protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
        }
    }

可以大概看到 inject 方法的处理, 参数分别为(当前的 bean 对象, bean 的名字, 属性值)

然后通过 beanFactory 来将依赖的值解析出来 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); 最后, 通过反射将该值”注”进去

			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}