测试代码
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
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
请求接口,响应符合预期。
很显然使用@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 {
}
如果该资源文件完全是可选的,则@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 {
}
应用可以正常启动。
只是接口的响应是空的。
设置编码
通过设置@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
修改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
只提供了一个默认的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 {
}
启动应用,请求接口,响应符合预期。
再将配置文件修改成Properties
文件格式进行测试,响应符合预期。
@PropertySource
读取指定配置文件与解决不兼容YAML
文件问题就介绍到这里,如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。