Spring 注解@PropertySource

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;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值