SpringBoot 常用讀取組態檔的 3 種方法!

2022-09-28 12:02:06

我們在SpringBoot框架進行專案開發中該如何優雅的讀取設定呢?或者說對於一些List或者Map應該如何設定呢?

本篇主要解決如下幾個問題:

1、Spring Boot有哪些常用的讀取組態檔方式?

1)使用 @Value 讀取組態檔

2) 使用 @ConfigurationProperties 讀取組態檔

3)使用 Environment 讀取組態檔

2、一些複雜的資料結構,如List、Map,如何設定?如何讀取呢?

前言

Spring Boot預設的組態檔有兩種格式: application.propertiesapplication.yml。 查詢順序是首先從application.properties 查詢,

如果找不到,再查詢 application.yml。優先順序:application.properties > application.yml。

以yml中rabbitmq的設定為例,組態檔如下:

rabbitmq:
  host: 127.0.0.1
  password: root
  port: 5672
  username: root

一、使用 @Value 讀取組態檔

這種方法適用於物件的引數比較少的情況

我們可以直接在物件的屬性上使用 @Value 註解,同時以 ${} 的形式傳入組態檔中對應的屬性。同時需要在該類的上方使用 @Configuration 註解,將該類作為設定

檔案加入,在啟動專案的時候實現注入。

@Configuration
public class RabbitmqProperties {

    @Value("${rabbitmq.host}")
    private String rabbitmqHost;
    @Value("${rabbitmq.port}")
    private String rabbitmqPort;
    @Value("${rabbitmq.username}")
    private String rabbitmqUsername;
    @Value("${rabbitmq.password}")
    private String rabbitmqPassword;
}

如果哪裡需要用到,通過 @Autowired 注入進去就可以獲取屬性值了

@Component
public class PropertiesTest {

    @Autowired
    private RabbitmqProperties rabbitmqProperties;
}

二、使用 @ConfigurationProperties 讀取組態檔

如果物件的引數比較多情況下,推薦使用 @ConfigurationProperties 會更簡單一些,不需要在每一個欄位的上面的使用@Value註解。

@ConfigurationProperties註解宣告當前類為設定讀取類

prefix="rabbitmq" 表示讀取字首為rabbitmq的屬性

範例如下:

@ConfigurationProperties(prefix = "rabbitmq")
public class RabbitmqProperties {
    private String host;
    private String port;
    private String username;
    private String password;
}

這裡有一點需要注意: 必須保證屬性名稱和欄位一模一樣,且類需要提供欄位的setter方法

注意 如果僅僅只是使用了 @ConfigurationProperties 註解是沒有效果的,它並不會將這個設定注入容器中,它還需要和注入容器的註解一起使用。

這裡有兩種方法實現將它注入到容器中

1、類上新增@Configuration註解

除了@Configuration,也可以是@Controller、@RestController、@Service、@Componet等註解,加入到Ioc容器裡。

@Setter
@Configuration
@ConfigurationProperties(prefix = "rabbitmq")
public class RabbitmqProperties {
    private String host;
    private String port;
    private String username;
    private String password;
}

同樣哪裡需要用到,通過 @Autowired 注入進去就可以獲取屬性值了

2、使用@EnableConfigurationProperties註解

通過 @EnableConfigurationProperties 註解可以將指定的設定讀取類的物件載入到Spring容器,也就是說,在其他設定類上使用一個@EnableConfigurationProperties註解,

來將組態檔的引數和RabbitmqProperties類的屬性繫結。這樣就不需要在RabbitmqProperties類上使用@Configuration註解了

@Configuration
@EnableConfigurationProperties(RabbitmqProperties.class)
public class RabbitmqConfig {

    @Autowired
    private RabbitmqProperties prop;
    
    @Bean
    public Rabbitmq rabbitmq() {
        Rabbitmq mq = new Rabbitmq();
        mq.setHost(prop.getHost());
        mq.setPort(prop.getPort());
        mq.setUsername(prop.getUsername());
        mq.setPassword(prop.getPassword());
        return mq;
    }
}

3、使用@ConfigurationPropertiesScan掃描

在 Spring Boot 2.2.0.RELEASE 中提供了一個掃描註解@ConfigurationPropertiesScan。它可以掃描特定包下所有的被@ConfigurationProperties標記的設定類,

並將它們進行IoC注入。

@ConfigurationPropertiesScan
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

三、使用 Environment 讀取組態檔

Environment 是 SpringCore 中的一個用於讀取組態檔的類,將此類使用 @Autowired 注入到類中就可以使用它的getProperty方法來獲取某個設定項的值。

如下程式碼所示:

@SpringBootApplication
public class MainApplication implements InitializingBean {
    
    @Autowired
    private Environment environment;

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }

    @Override
    public void afterPropertiesSet() {
        String username = environment.getProperty("rabbitmq.username");
        System.out.println("rabbitmq當前使用者名稱為:  " + username);
    }
}

四、常用的幾種資料結構設定讀取

比如我們常見的字串、整數、List、Map如何設定和讀取呢?這裡以之前自己開發的一個專案中用到複雜的設定資料來做範例之前有釋出一個基於

Spring Cache實現二級快取(Caffeine+Redis) 的專案,裡面的設定就比較複雜。

具體專案地址:基於Spring Cache實現分散式二級快取

這裡展示設定範例:

application.yml

# 二級快取設定
l2cache:
  config:
    # 是否儲存空值,預設true,防止快取穿透
    allowNullValues: true
    # 組合快取設定
    composite:
      # 是否全部啟用一級快取,預設false
      l1AllOpen: false
      # 是否手動啟用一級快取,預設false
      l1Manual: true
      # 手動設定走一級快取的快取key集合,針對單個key維度
      l1ManualKeySet:
      - userCache:user01
      - userCache:user02
      - userCache:user03
      - userCache:user04
      
    # 一級快取
    caffeine:
      # 快取重新整理排程執行緒池的大小
      refreshPoolSize: 2
      # 寫入後過期時間(秒)
      expireAfterWrite: 180
      # 存取後過期時間(秒)
      expireAfterAccess: 180
      # 初始化大小
      initialCapacity: 100
      # 最大快取物件個數,超過此數量時之前放入的快取將失效
      maximumSize: 300
      
    # 二級快取
    redis:
      # 全域性過期時間,單位毫秒,預設不過期
      defaultExpiration: 300000
      # 每個cacheName的過期時間,單位毫秒
      expires: {userCache: 300000,goodsCache: 50000}
      # 快取更新時通知其他節點的topic名稱 預設 cache:redis:caffeine:topic
      topic: cache:redis:caffeine:topic

設定實體類

@Data
@ConfigurationProperties(prefix = "l2cache")
public class L2CacheProperties {
    /**
     * 快取設定
     */
    private L2CacheConfig config;
}

L2CacheConfig

/**
 *  快取設定
 * 
 * @author xub
 * @date 2022/9/20 下午6:02
 */
@Data
public class L2CacheConfig {


    /** 是否儲存空值,設定為true時,可防止快取穿透 */
    private boolean allowNullValues = true;
    
    /** 組合快取設定 */
    private final Composite composite = new Composite();
    
    /** 一級快取設定 */
    private final Caffeine caffeine = new Caffeine();

    /** 二級快取設定 */
    private final Redis redis = new Redis();

    /**
     * 組合快取設定
     */
    @Data
    public static class Composite {

        /** 是否全部啟用一級快取,預設false*/
        private boolean l1AllOpen = false;

        /** 是否手動啟用一級快取,預設false */
        private boolean l1Manual = false;

        /** 手動設定走一級快取的快取key集合,針對單個key維度*/
        private Set<String> l1ManualKeySet = new HashSet<>();
    }
    
    /**
     * 一級快取設定
     */
    @Data
    public static class Caffeine {
  
        /** 快取重新整理排程執行緒池的大小 預設為 CPU數 * 2 */
        private Integer refreshPoolSize = Runtime.getRuntime().availableProcessors();

        /** 寫入後過期時間,單位秒 */
        private long expireAfterWrite;

        /** 寫入後重新整理時間,單位秒 */
        private long refreshAfterWrite;

        /** 初始化大小 */
        private int initialCapacity;

        /** 最大快取物件個數,超過此數量時之前放入的快取將失效 */
        private long maximumSize;
    }

    /**
     * 二級快取設定
     */
    @Data
    public static class Redis {

        /** 全域性過期時間,單位毫秒,預設不過期 */
        private long defaultExpiration = 0;

        /** 每個cacheName的過期時間,單位毫秒,優先順序比defaultExpiration高 */
        private Map<String, Long> expires = new HashMap<>();

        /** 快取更新時通知其他節點的topic名稱 */
        private String topic = "cache:redis:caffeine:topic";

    }
}

Configuration類

@Configuration
@EnableConfigurationProperties(L2CacheProperties.class)
public class CacheRedisCaffeineAutoConfiguration {

   @Autowired
   private L2CacheProperties l2CacheProperties;

   @Bean
   public RedisCaffeineCacheManager cacheManager() {
      return new RedisCaffeineCacheManager(l2CacheProperties.getConfig());
   }
}

測試

專案啟動後,進行Debug打斷點,看有木有注入到L2CacheProperties實體中

通過上面截圖就可以看出,所有application.yml設定資料,都已經在L2CacheProperties實體中,說明設定成功,獲取也成功了。