Spring Boot讀取自定義配置文件

@Value

首先,會想到使用@Value註解,該註解隻能去解析yaml文件中的簡單類型,並綁定到對象屬性中去。

felord:
  phone: 182******32
  def:
    name: 碼農小胖哥
    blog: felord.cn
    we-chat: MSW_623
  dev:
    name: 碼農小胖哥
    blog: felord.cn
    we-chat: MSW_623
  type: JUEJIN

對於上面的yaml配置,如果我們使用@Value註解的話,冒號後面直接有值的key才能正確註入對應的值。例如felord.phone我們可以通過@Value獲取,但是felord.def不行,因為felord.def後面沒有直接的值,它還有下一級選項。另外@Value不支持yaml松散綁定語法,也就是說felord.def.weChat獲取不到felord.def.we-chat的值。

@Value是通過使用Spring的SpEL表達式來獲取對應的值的:

// 獲取 yaml 中 felord.phone的值 並提供默認值 UNKNOWN
@Value("${felord.phone:UNKNOWN}")
 private String phone;

@Value的使用場景是隻需要獲取配置文件中的某項值的情況下,如果我們需要將一個系列的值進行綁定註入就建議使用復雜對象的形式進行註入瞭。

@ConfigurationProperties

@ConfigurationProperties註解提供瞭我們將多個配置選項註入復雜對象的能力。它要求我們指定配置的共同前綴。比如我們要綁定felord.def下的所有配置項:

package cn.felord.yaml.properties;
 
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
 
import static cn.felord.yaml.properties.FelordDefProperties.PREFIX;
 
/**
 * @author felord.cn
 */
@Data
@ConfigurationProperties(PREFIX)
public class FelordDefProperties {
    static final String PREFIX = "felord.def";
    private String name;
    private String blog;
    private String weChat;
}

我們註意到我們可以使用weChat接收we-chat的值,因為這種形式支持從駝峰camel-case到短橫分隔命名kebab-case的自動轉換。

如果我們使用@ConfigurationProperties的話建議配置類命名後綴為Properties,比如Redis的後綴就是RedisProperties,RabbitMQ的為RabbitProperties。

另外我們如果想進行嵌套的話可以借助於@NestedConfigurationProperty註解實現。也可以借助於內部類。這裡用內部類實現將開頭yaml中所有的屬性進行註入:

package cn.felord.yaml.properties;
 
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
 
import static cn.felord.yaml.properties.FelordProperties.PREFIX;
 
 
/**
 * 內部類和枚舉配置.
 *
 * @author felord.cn
 */
@Data
@ConfigurationProperties(PREFIX)
public class FelordProperties {
 
    static final String PREFIX = "felord";
    private Def def;
    private Dev dev;
    private Type type;
 
    @Data
    public static class Def {
        private String name;
        private String blog;
        private String weChat;
    }
 
    @Data
    public static class Dev {
        private String name;
        private String blog;
        private String weChat;
    }
 
    public enum Type {
        JUEJIN,
        SF,
        OSC,
        CSDN
    }
}

單獨使用@ConfigurationProperties的話依然無法直接使用配置對象FelordDefProperties,因為它並沒有被註冊為Spring Bean。我們可以通過兩種方式來使得它生效。

顯式註入 Spring IoC

你可以使用@Component、@Configuration等註解將FelordDefProperties註入Spring IoC使之生效。

package cn.felord.yaml.properties;
 
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
import static cn.felord.yaml.properties.FelordDefProperties.PREFIX;
 
/**
 * 顯式註入Spring IoC
 * @author felord.cn
 */
@Data
@Component
@ConfigurationProperties(PREFIX)
public class FelordDefProperties {
    static final String PREFIX = "felord.def";
    private String name;
    private String blog;
    private String weChat;
}

@EnableConfigurationProperties

我們還可以使用註解@EnableConfigurationProperties進行註冊,這樣就不需要顯式聲明配置類為Spring Bean瞭。

package cn.felord.yaml.configuration;
 
import cn.felord.yaml.properties.FelordDevProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
 
/**
 * 使用 {@link EnableConfigurationProperties} 註冊 {@link FelordDevProperties}使之生效
 * @author felord.cn
 */
@EnableConfigurationProperties({FelordDevProperties.class})
@Configuration
public class FelordConfiguration {
}

該註解需要顯式的註冊對應的配置類。

@ConfigurationPropertiesScan

在Spring Boot 2.2.0.RELEASE中提供瞭一個掃描註解@ConfigurationPropertiesScan。它可以掃描特定包下所有的被@ConfigurationProperties標記的配置類,並將它們進行IoC註入。

package cn.felord.yaml;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 
/**
 * {@link ConfigurationPropertiesScan} 同 {@link EnableConfigurationProperties} 二選一
 *
 * @see cn.felord.yaml.configuration.FelordConfiguration
 * @author felord.cn
 */
@ConfigurationPropertiesScan
@SpringBootApplication
public class SpringBootYamlApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringBootYamlApplication.class, args);
    }
 
}

這非常適合自動註入和批量註入配置類的場景,但是有版本限制,必須在2.2.0及以上。

@PropertySource註解

@PropertySource可以用來加載指定的配置文件,默認它隻能加載*.properties文件,不能加載諸如yaml等文件。

@PropertySource相關屬性介紹

  • value:指明加載配置文件的路徑。
  • ignoreResourceNotFound:指定的配置文件不存在是否報錯,默認是false。當設置為 true 時,若該文件不存在,程序不會報錯。實際項目開發中,最好設置 ignoreResourceNotFound 為 false。
  • encoding:指定讀取屬性文件所使用的編碼,我們通常使用的是UTF-8。
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Configuration
@PropertySource(value = {"classpath:common.properties"},ignoreResourceNotFound=false,encoding="UTF-8")
@ConfigurationProperties(prefix = "author")
public class Author {
    private String name;
    private String job;
    private String sex;
}

有小夥伴也許發現示例上的@ConfigurationProperties註解瞭。當我們使用@Value需要註入的值較多時,代碼就會顯得冗餘。我們可以使用@ConfigurationProperties 中的 prefix 用來指明我們配置文件中需要註入信息的前綴

前邊提到瞭用@PropertySource隻能加載*.properties文件,但如果我們項目的配置文件不是*.properties這種類型,而是其他類型,諸如yaml,此時我們可以通過實現PropertySourceFactory接口,重寫createPropertySource方法,就能實現用@PropertySource也能加載yaml等類型文件。

public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(String sourceName, EncodedResource resource) throws IOException {
        Properties propertiesFromYaml = loadYaml(resource);
        if(StringUtils.isBlank(sourceName)){
            sourceName =  resource.getResource().getFilename();;
        }

        return new PropertiesPropertySource(sourceName, propertiesFromYaml);
    }
    private Properties loadYaml(EncodedResource resource) throws FileNotFoundException {
        try {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();
            return factory.getObject();
        } catch (IllegalStateException e) {
            // for ignoreResourceNotFound
            Throwable cause = e.getCause();
            if (cause instanceof FileNotFoundException)
                throw (FileNotFoundException) e.getCause();
            throw e;
        }
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Configuration
@PropertySource(factory = YamlPropertySourceFactory.class,value = {"classpath:user.yml"},ignoreResourceNotFound=false,encoding="UTF-8")
@ConfigurationProperties(prefix = "user")
public class User {
    private String username;

    private String password;
}

使用EnvironmentPostProcessor加載自定義配置文件

1、實現EnvironmentPostProcessor接口,重寫postProcessEnvironment方法

@Slf4j
public class CustomEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

        Properties properties = new Properties();

        try {
            properties.load(new InputStreamReader(CustomEnvironmentPostProcessor.class.getClassLoader().getResourceAsStream("custom.properties"),"UTF-8"));

            PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("custom",properties);
            environment.getPropertySources().addLast(propertiesPropertySource);
        } catch (IOException e) {
          log.error(e.getMessage(),e);
        }

    }
}

2、在META-INF下創建spring.factories

spring.factories文件內容如下:
org.springframework.boot.env.EnvironmentPostProcessor=com.github.lybgeek.env.CustomEnvironmentPostProcessor

1、2步驟實現完後,就可以在代碼中直接用@Value的方式獲取自定義配置文件內容瞭

讀取的自定義配置文件內容的實現方法有多種多樣,除瞭上面的方法,還可以在以-jar方式啟動時,執行形如下命令

java -jar project.jar –spring.config.location=classpath:/config/custom.yml

也能實現。還可以幹脆自定義配置文件都以application-*為前綴,比如application-custom,然後在application.properties,使用spring.profiles.include=custom或者spring.profiles.active=custom也可以實現

總結

日常開發中單個屬性推薦使用@Value,如果同一組屬性為多個則推薦@ConfigurationProperties。需要補充一點的是@ConfigurationProperties還支持使用 JSR303 進行屬性校驗。好瞭今天的教程就到這裡

相關的demo地址

https://gitee.com/felord/spring-boot-yml.git

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-outside-config

以上就是Spring Boot讀取自定義配置文件的詳細內容,更多關於Spring Boot讀取自定義配置文件的資料請關註WalkonNet其它相關文章!

推薦閱讀: