在sprinboot中使用@PropertySource注解读取yml文件


一、使用

1、自定义 YmlPropertySourceFactory

public class YmlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        String sourceName = resource.getResource().getFilename();
        
        if (StringUtils.isNotBlank(sourceName) && (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml"))) {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();
            Properties properties = factory.getObject();

            return new PropertiesPropertySource(name,properties);
        }
        return null;
    }

2、使用@PropertySource

注入外部文件,将factory 设置为 YmlPropertySourceFactory

使用绝对路径用 file:开头

@PropertySource(name = "test", value = "file:C:\\Users\\x\\Desktop\\test.yml", factory = YmlPropertySourceFactory.class)

使用类路径用classpath:开头

@PropertySource(name = "test", value = "classpath:test.yml", factory = YmlPropertySourceFactory.class)


@PropertySource(name = "test", value = "classpath:test.yml", factory = YmlPropertySourceFactory.class)
@Component
@Data
public class TestProperties {

    @Value("${test.key1}")
    private String key1;

    @Value("${test.key2}")
    private String key2;
}

3、test.yml

写一些yml格式的参数

test:
  key1: value1
  key2: value2

二、原理

1、在spring 的 ConfigurationClassParser 类中的 doProcessConfigurationClass( ) 方法,会对配置类进行解析,也会解析@PropertySource 注解

	//获取 @PropertySources、@PropertySource
	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");
			}
		}

2、processPropertySource(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 配置文件找不到的时候忽略异常
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
		
		//factory 用于创建 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 resource = this.resourceLoader.getResource(resolvedLocation);
				//创建PropertySource,添加到上下文环境中
				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;
				}
			}
		}
	}

3、getResource( )

	@Override
	public Resource getResource(String location) {
		if (this.resourceLoader != null) {
			return this.resourceLoader.getResource(location);
		}
		return super.getResource(location);
	}
	@Override
	public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");

		for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
			Resource resource = protocolResolver.resolve(location, this);
			if (resource != null) {
				return resource;
			}
		}
		// 以/开头
		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
		//以classpath:开头
		else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			try {
				// Try to parse the location as a URL...
				URL url = new URL(location);
				return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
			}
			catch (MalformedURLException ex) {
				// No URL -> resolve as resource path.
				return getResourceByPath(location);
			}
		}
	}

5、createPropertySource( )

回调 YmlPropertySourceFactory 的 createPropertySource( ) 方法

6、addPropertySource( )

	private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		//获取上下文环境中的 propertySources 
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			//环境中已经存在同名的 propertySource
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				//如果是复合的 CompositePropertySource ,将新的 newSource 直接加入复合的
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					//如果是普通的 ResourcePropertySource 
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					//创建复合的将 existing 和 newSource 都加入复合的
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					//用复合的 CompositePropertySource 替换掉环境中原来的
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		//加入环境中的 propertySources 中
		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
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值