0
点赞
收藏
分享

微信扫一扫

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题


测试代码

​pom.xml​​:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
</parent>

<packaging>jar</packaging>

<groupId>com.kaven</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>springboot</name>
<description>springboot</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

​user.properties​​:

user.username=kaven
user.password=itkaven
user.hobbies[0]=打乒乓球
user.hobbies[1]=跑步
user.hobbies[2]=比赛
user.scores.mathematics=150
user.scores.english=100
user.user-token[0].token=jdsnjvd430=bkjgfnmbj
user.user-token[1].token=4imkmklf034mflkmfg-3=
user.user-token[2].token=mgklfmj498mnmgjnmfnjj

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_spring


​UserToken​​​类(用户​​Token​​):

package com.kaven.springboot.config;

import lombok.Setter;
import lombok.ToString;

@Setter
@ToString
public class UserToken {
private String token;
}

​UserProperties​​类(用户参数):

package com.kaven.springboot.config;

import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;
import java.util.Map;
import java.util.Set;

@ConfigurationProperties(prefix = "user")
@ToString
@Setter
public class UserProperties {
private String username;
private String password;
private Set<String> hobbies;
private Map<String, Integer> scores;
private List<UserToken> userToken;
}

​UserConfig​​类(用于读取指定配置文件):

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/user.properties")
public class UserConfig {

}

接口(获取用户参数):

package com.kaven.springboot.controller;

import com.google.gson.Gson;
import com.kaven.springboot.config.UserProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class ConfigController {

private static final Gson GSON = new Gson();

@Resource
private UserProperties userProperties;

@GetMapping("/config")
public String getConfig() {
String result = GSON.toJson(userProperties);
System.out.println(result);
return result;
}
}

启动类:

package com.kaven.springboot;

import com.kaven.springboot.config.UserProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan(basePackageClasses = {UserProperties.class})
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(SpringbootApplication.class);
application.run(args);
}
}

启动应用,使用​​Postman​​请求接口,响应符合预期。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_spring boot_02


很显然使用​​@PropertySource​​​注解读取到了指定配置文件,并通过​​@ConfigurationProperties​​​注解将配置文件中的配置项绑定到了​​UserProperties​​实例。

忽略未找到资源

当未找到​​@PropertySource​​注解指定的资源时,启动应用会报错。

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/1.properties")
public class UserConfig {

}

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_配置文件_03


如果该资源文件完全是可选的,则​​@PropertySource​​​注解的​​ignoreResourceNotFound​​​属性设置为​​true​​是合适的。

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/1.properties", ignoreResourceNotFound = true)
public class UserConfig {

}

应用可以正常启动。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_spring_04


只是接口的响应是空的。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_配置文件_05

设置编码

通过设置​​@PropertySource​​​注解的​​encoding​​属性来设置编码,即指定资源的特定字符编码。

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/1.properties", ignoreResourceNotFound = true, encoding = "UTF-8")
public class UserConfig {

}

读取YAML文件存在问题

将配置文件修改成​​YAML​​文件格式。

user:
username: kaven
password: itkaven
hobbies:
- "打乒乓球"
- "跑步"
- "比赛"
scores:
mathematics: 150
english: 100
user-token:
- token: jdsnjvd430=bkjgfnmbj
- token: 4imkmklf034mflkmfg-3=
- token: mgklfmj498mnmgjnmfnjj

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_配置文件_06


修改​​UserConfig​​:

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/user.yml", ignoreResourceNotFound = true, encoding = "UTF-8")
public class UserConfig {

}

启动应用,请求接口,此时响应为空。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_配置文件_07


​Spring​​​只提供了一个默认的​​PropertySourceFactory​​​,即​​DefaultPropertySourceFactory​​类。

package org.springframework.core.io.support;

import java.io.IOException;
import org.springframework.core.env.PropertySource;
import org.springframework.lang.Nullable;

public class DefaultPropertySourceFactory implements PropertySourceFactory {
public DefaultPropertySourceFactory() {
}

public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource);
}
}

不难发现这个工厂是用​​PropertiesLoaderUtils.loadProperties(resource)​​来加载属性文件。

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;
}

继续看源码可以发现​​Spring​​​提供的​​@PropertySource​​​注解并没有兼容​​YAML​​​文件的解析。​​@PropertySource​​​注解中,有一个​​factory​​​属性,可指定一个自定义的​​PropertySourceFactory​​​接口实现,用于解析指定的文件。默认的实现是​​DefaultPropertySourceFactory​​​类,使用​​Properties​​进行解析。

自定义PropertySourceFactory兼容YAML文件

​CompositePropertySourceFactory​​​类(继承​​DefaultPropertySourceFactory​​类):

package com.kaven.springboot.config;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;

import java.io.IOException;
import java.util.Optional;
import java.util.Properties;

public class CompositePropertySourceFactory extends DefaultPropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource)
throws IOException {
String sourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());

// sourceName不能为空
assert sourceName != null;

// 源文件不存在
if (!resource.getResource().exists()) {
// 返回一个空的属性
return new PropertiesPropertySource(sourceName, new Properties());
}
// 源文件存在
else {
// yaml文件格式解析
if (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml")) {
Properties propertiesFromYaml = loadYaml(resource);
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
// 其他文件格式解析
// 委托给父类
else {
return super.createPropertySource(name, resource);
}
}
}

private Properties loadYaml(EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
}
}

修改​​UserConfig​​:

package com.kaven.springboot.config;

import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:/static/user.yml", ignoreResourceNotFound = true, encoding = "UTF-8", factory = CompositePropertySourceFactory.class)
public class UserConfig {

}

启动应用,请求接口,响应符合预期。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_java_08


再将配置文件修改成​​Properties​​文件格式进行测试,响应符合预期。

Spring Boot:@PropertySource读取指定配置文件与解决不兼容YAML文件问题_spring boot_09


​@PropertySource​​​读取指定配置文件与解决不兼容​​YAML​​文件问题就介绍到这里,如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。


举报

相关推荐

0 条评论