spring-context注解源码系列十——@PropertySource

spring-context注解源码系列十——@PropertySource

注解说明

Annotation providing a convenient and declarative mechanism for adding a {@link org.springframework.core.env.PropertySource PropertySource} to Spring’s {@link org.springframework.core.env.Environment Environment}.

提供了一种注解的机制,将配置文件中配置信息合并到spring的配置信息中。

PropertySource可以将任意位置的配置文件合并到项目中,包括远程URL文件、服务器任意位置的文件等,只要是sping ResourceLoader可以加载到的配置文件都可以支持。

PropertySource默认支持读取properties文件和xml文件,通过传入自定义的factory也可以实现读取yaml文件。

属性说明

	/**
	 * 配置文件的名称,用于配置文件的合并,如果不传入则自动生成
	 */
	String name() default "";

	/**
	 * 配置文件路径,用于读取并解析配置信息
	 */
	String[] value();

	/**
	 * 是否忽略找不到,ignoreResourceNotFound=true则不会在找不到文件的时候抛出异常
	 */
	boolean ignoreResourceNotFound() default false;

	/**
	 * 文件编码,不传则默认为utf-8
	 */
	String encoding() default "";

	/**
	 * 自定义资源读取工程,不传则使用 org.springframework.core.io.support.DefaultPropertySourceFactory
	 */
	Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;

使用示例

@Configuration
@PropertySource(value = {"db.properties", "config.properties"})
public class ConditionalConfig {

    public ConditionalService conditionalService() {
        return new ConditionalService();
    }
}

相关源码

ConfigurationClassParser

	/**
	 * Apply processing and build a complete {@link ConfigurationClass} by reading the
	 * annotations, members and methods from the source class. This method can be called
	 * multiple times as relevant sources are discovered.
	 * @param configClass the configuration class being build
	 * @param sourceClass a source class
	 * @return the superclass, or {@code null} if none found or previously processed
	 *
	 * 读取配置类中的注解、内部类、方法来完成配置类的解析
	 */
	@Nullable
	protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		......

		// Process any @PropertySource annotations
		// 处理 PropertySource 注解
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			// 在 environment可配置的情况下,对每一个@PropertySource进行处理
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		......
		return null;
	}
	/**
	 * Process the given <code>@PropertySource</code> annotation metadata.
	 * @param propertySource metadata for the <code>@PropertySource</code> annotation found
	 * @throws IOException if loading a property source failed
	 * 处理@PropertySource
	 */
	private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		// 解析 name 属性,没有则自动创建一个,用于配置信息合并
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		// 解析 encoding 属性
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		// 解析 value 属性,如果该属性为空,则会抛出异常
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		// 解析 ignoreResourceNotFound 属性,ignoreResourceNotFound=true则不会抛出异常
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		// 解析 factory 属性,如果未设置,则使用默认的 DefaultPropertySourceFactory,否则使用传入的工厂
		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

		// 根据配置的资源地址,逐个加载并设置给environment
		for (String location : locations) {
			try {
				// 使用environment信息解析传入的配置文件地址,得到真正的地址
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				// 使用资源加载器,自动识别并得到资源
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				// 创建资源并调用addPropertySource合并到当前的environment中
				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
				// 如果忽略资源找不到的情况(ignoreResourceNotFound=true),则不抛出异常
				if (ignoreResourceNotFound) {
					if (logger.isInfoEnabled()) {
						logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
					}
				}
				else {
					throw ex;
				}
			}
		}
	}
// 将加载到的属性设置给environment
	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
			// propertySourceNames存在表示之前已经存储起来了,需要进行合并
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				if (existing instanceof CompositePropertySource) {
					// 已经存在的如果是合并过(CompositePropertySource类型)的,则直接进行合并
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					// 如果已经存在是未合并的,则新建一个CompositePropertySource并合2个propertySource
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		// 将 propertySource 存储起来
		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);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值