天天看點

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檔案問題

​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檔案問題

很顯然使用​

​@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檔案問題

如果該資源檔案完全是可選的,則​

​@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 Boot:@PropertySource讀取指定配置檔案與解決不相容YAML檔案問題

設定編碼

通過設定​

​@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檔案問題

修改​

​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檔案問題

​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檔案問題

再将配置檔案修改成​

​Properties​

​檔案格式進行測試,響應符合預期。

Spring Boot:@PropertySource讀取指定配置檔案與解決不相容YAML檔案問題