Spring 注解@PropertySource
- ConfigurationClassPostProcessor回调postProcessBeanDefinitionRegistry向容器中注册bean定义;
- 构建ConfigurationClassParser 解析配置类调用doProcessConfigurationClass从源类中读取注释、成员和方法处理@PropertySources;
- 解析处理给定的@PropertySource注释元数据;
- 将生成的PropertySource属性源对象添加到ConfigurableEnvironment中;
@PropertySources 聚合多个PropertySource
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PropertySources {
PropertySource[] value();
}
@PropertySource 为向Spring的环境添加PropertySource提供了一种方便的声明性机制。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
*指示此属性源的名称,如果为空,有PropertySourceFactory工厂创建。
*/
String name() default "";
/**
*指示要加载的属性文件的资源位置
*/
String[] value();
/**
*指示是否应忽略查找属性资源的失败。如果属性文件是完全可选的,那么true是合适的。默认值为false。
*/
boolean ignoreResourceNotFound() default false;
/**
*给定资源的特定字符编码,例如“UTF-8”。
*/
String encoding() default "";
/**
* 指定自定义PropertySourceFactory。
* 默认情况下,将使用标准资源文件的默认工厂。
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
ConfigurationClassParser
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
}
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
// 获取名称用于PropertySource 的name 属性赋值
String name = propertySource.getString("name");
//如果名称为空 有工厂创建
if (!StringUtils.hasLength(name)) {
name = null;
}
//获取注解propertySource的encoding编码值
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");
//忽略不能加载到的资源---上面的locations 集合中的属性文件的资源位置不存在是否忽略校验
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
//获取工厂类---用于创建propertySource,locations 每个资源就会创建一个propertySource
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);
//通过工厂创建PropertySource 添加到上下文环境中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
// 尝试打开占位符时无法解析或找不到资源
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)) {
// 已经添加了一个版本,需要扩展它
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();
}
//创建组合PropertySource
CompositePropertySource composite = new CompositePropertySource(name);
//首先天添加新的
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
//根据给定的名称替换为新创建的composite
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
//放在最后
propertySources.addLast(propertySource);
}
else {
//放在当前propertySourceNames中最后一个propertySource的前面
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
PropertySourceFactory的默认实现,将每个资源包装在ResourcePropertySource中。
public class DefaultPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
}
PropertiesPropertySource的子类,该子类从给定的资源或资源位置(如“classpath://myco/foo.Properties”或“file:/path/to/file.xml”)加载Properties对象。
支持传统的和基于XML的属性文件格式;但是,为了使XML处理生效,底层Resource的getFilename()方法必须返回一个以“.XML”结尾的非null值。PropertiesLoaderUtils.loadProperties(resource)加载返回一个Properties用于PropertiesPropertySource 的source中
public class ResourcePropertySource extends PropertiesPropertySource {
@Nullable
private final String resourceName;
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
public ResourcePropertySource(EncodedResource resource) throws IOException {
super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = null;
}
}
PropertySources接口的默认实现。允许操作包含的属性源,并提供用于复制现有PropertySources实例的构造函数。
在addFirst和addLast等方法中提到优先级的情况下,这与使用PropertyResolver解析给定属性时搜索属性源的顺序有关。
这里提示一下:Spring 在获取解析配置值时,循环propertySources 取出每个PropertySource<?> 从属性源中根据键(key)找值(value)如果找到就结束循环 。所以有propertySourceList 的索引下标越小优先级越高
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
/**
* 添加具有最高优先级的给定属性源对象
*/
public void addFirst(PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
}
/**
*添加优先级最低的给定属性源对象
*/
public void addLast(PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
}
/**
*添加优先级直接高于命名的相对属性源的给定属性源对象
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index, propertySource);
}
}
/**
*添加优先级直接低于命名的相对属性源的给定属性源对象
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}
}
}
包含一个或多个PropertySource对象的持有者。
public interface PropertySources extends Iterable<PropertySource<?>> {
default Stream<PropertySource<?>> stream() {
return StreamSupport.stream(spliterator(), false);
}
boolean contains(String name);
@Nullable
PropertySource<?> get(String name);
}
在一组PropertySource实例上迭代的复合PropertySource实现。在多个属性源共享同一名称的情况下是必要的,例如向@PropertySource提供多个值时。
public class CompositePropertySource extends EnumerablePropertySource<Object> {
private final Set<PropertySource<?>> propertySources = new LinkedHashSet<>();
public void addPropertySource(PropertySource<?> propertySource) {
this.propertySources.add(propertySource);
}
public void addFirstPropertySource(PropertySource<?> propertySource) {
List<PropertySource<?>> existing = new ArrayList<>(this.propertySources);
this.propertySources.clear();
this.propertySources.add(propertySource);
this.propertySources.addAll(existing);
}
public Collection<PropertySource<?>> getPropertySources() {
return this.propertySources;
}
}
上面代码看到可以往集合中添加PropertySource 指定位置等,下面来看一下优先级的具体解析逻辑:
- AbstractEnvironment
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String resolvePlaceholders(String text) {
return this.propertyResolver.resolvePlaceholders(text);
}
- AbstractPropertyResolver
@Override
public String resolvePlaceholders(String text) {
if (this.nonStrictHelper == null) {
//
this.nonStrictHelper = createPlaceholderHelper(true);
}
return doResolvePlaceholders(text, this.nonStrictHelper);
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
解析占位符详情请查看:Spring Environment
PropertyPlaceholderHelper
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
//。。。。。
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
//。。。。。。
}
循环propertySources 获取每个propertySource属性源,从属性源中根据key 查找value,一旦找到就不再循环后面的propertySource。
- PropertySourcesPropertyResolver
@Override
@Nullable
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
//循环propertySources
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;
}