測試代碼
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
檔案格式進行測試,響應符合預期。