spring boot最佳實戰2--外部配置

weixin_33895657發表於2016-06-19

Spring Boot允許外化(externalize)你的配置,這樣你能夠在不同的環境下使用相同的程式碼。你可以使用properties檔案,YAML檔案,環境變數和命令列引數來外化配置。使用@Value註解,可以直接將屬性值注入到你的beans中,並通過Spring的Environment抽象或繫結到結構化物件來訪問。

Spring Boot使用一個非常特別的PropertySource次序來允許對值進行合理的覆蓋,需要以下面的次序考慮屬性:

命令列引數
來自於java:comp/env的JNDI屬性
Java系統屬性(System.getProperties())
作業系統環境變數
只有在random.*裡包含的屬性會產生一個RandomValuePropertySource
在打包的jar外的應用程式配置檔案(application.properties,包含YAML和profile變數)
在打包的jar內的應用程式配置檔案(application.properties,包含YAML和profile變數)
在@Configuration類上的@PropertySource註解
預設屬性(使用SpringApplication.setDefaultProperties指定)

下面是一個具體的示例(假設你開發一個使用name屬性的@Component):

import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
 @Value("${name}") private String name; 
}

你可以將一個application.properties檔案捆綁到jar內,用來提供一個合理的預設name屬性值。當執行在生產環境時,可以在jar外提供一個application.properties檔案來覆蓋name屬性。對於一次性的測試,你可以使用特定的命令列開關啟動(比如,java -jar app.jar --name="Spring")。

配置隨機值
RandomValuePropertySource在注入隨機值(比如,金鑰或測試用例)時很有用。它能產生整數,longs或字串,比如:

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

random.int*語法是OPEN value (,max) CLOSE,此處OPEN,CLOSE可以是任何字元,並且value,max是整數。如果提供max,那麼value是最小的值,max是最大的值(不包含在內)。

訪問命令列屬性

預設情況下,SpringApplication將任何可選的命令列引數(以'--'開頭,比如,--server.port=9000)轉化為property,並將其新增到Spring Environment中。如上所述,命令列屬性總是優先於其他屬性源。
如果你不想將命令列屬性新增到Environment裡,你可以使用SpringApplication.setAddCommandLineProperties(false)來禁止它們。

application屬性檔案

SpringApplication將從以下位置載入application.properties檔案,並把它們新增到Spring Environment中:
當前目錄下的一個/config子目錄
當前目錄
一個classpath下的/config包
classpath根路徑(root)

這個列表是按優先順序排序的(列表中位置高的將覆蓋位置低的)。
注:你可以使用YAML('.yml')檔案替代'.properties'。
如果不喜歡將application.properties作為配置檔名,你可以通過指定spring.config.name環境屬性來切換其他的名稱。你也可以使用spring.config.location環境屬性來引用一個明確的路徑(目錄位置或檔案路徑列表以逗號分割)。
$ java -jar myproject.jar --spring.config.name=myproject//or$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

如果spring.config.location包含目錄(相對於檔案),那它們應該以/結尾(在載入前,spring.config.name產生的名稱將被追加到後面)。不管spring.config.location是什麼值,預設的搜尋路徑classpath:,classpath:/config,file:,file:config/總會被使用。以這種方式,你可以在application.properties中為應用設定預設值,然後在執行的時候使用不同的檔案覆蓋它,同時保留預設配置。
注:如果你使用環境變數而不是系統配置,大多數作業系統不允許以句號分割(period-separated)的key名稱,但你可以使用下劃線(underscores)代替(比如,使用SPRING_CONFIG_NAME代替spring.config.name)。如果你的應用執行在一個容器中,那麼JNDI屬性(java:comp/env)或servlet上下文初始化引數可以用來取代環境變數或系統屬性,當然也可以使用環境變數或系統屬性。

特定的Profile屬性

除了application.properties檔案,特定配置屬性也能通過命令慣例application-{profile}.properties來定義。特定Profile屬性從跟標準application.properties相同的路徑載入,並且特定profile檔案會覆蓋預設的配置。

屬性佔位符

當application.properties裡的值被使用時,它們會被存在的Environment過濾,所以你能夠引用先前定義的值(比如,系統屬性)。
app.name=MyAppapp.description=${app.name} is a Spring Boot application

使用YAML代替Properties

YAML是JSON的一個超集,也是一種方便的定義層次配置資料的格式。無論你何時將SnakeYAML 庫放到classpath下,SpringApplication類都會自動支援YAML作為properties的替換。

載入YAML
Spring框架提供兩個便利的類用於載入YAML文件,YamlPropertiesFactoryBean會將YAML作為Properties來載入,YamlMapFactoryBean會將YAML作為Map來載入。
示例:

environments:
    dev:
        url: http://dev.bar.com
        name: Developer Setup
    prod:
        url: http://foo.bar.com
        name: My Cool App

上面的YAML文件會被轉化到下面的屬性中:

environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App

YAML列表被表示成使用[index]間接引用作為屬性keys的形式,例如下面的YAML:

my:
   servers:
       - dev.bar.com
       - foo.bar.com

將會轉化到下面的屬性中:
my.servers[0]=dev.bar.commy.servers[1]=foo.bar.com

使用@ConfigurationProperties繫結配置到bean時,你需要確定目標bean中有個java.util.List或Set型別的屬性,並且需要提供一個setter或使用可變的值初始化它,比如,下面的程式碼將繫結上面的屬性:

@ConfigurationProperties(prefix="my")
public class Config {
 private List<String> servers = new ArrayList<String>(); 
public List<String> getServers() {
 return this.servers;
 }}

在Spring環境中使用YAML暴露屬性
YamlPropertySourceLoader類能夠用於將YAML作為一個PropertySource匯出到Sprig Environment。這允許你使用熟悉的@Value註解和佔位符語法訪問YAML屬性。

Multi-profile YAML文件
你可以在單個檔案中定義多個特定配置(profile-specific)的YAML文件,並通過一個spring.profiles key標示應用的文件。例如:

server:
    address: 192.168.1.100
---
spring:
    profiles: development
server:
    address: 127.0.0.1
---
spring:
    profiles: production
server:
    address: 192.168.1.120

在上面的例子中,如果development配置被啟用,那server.address屬性將是127.0.0.1。如果development和production配置(profiles)沒有啟用,則該屬性的值將是192.168.1.100。

YAML缺點
YAML檔案不能通過@PropertySource註解載入。所以,在這種情況下,如果需要使用@PropertySource註解的方式載入值,那就要使用properties檔案。

型別安全的配置屬性
使用@Value("${property}")註解注入配置屬性有時可能比較笨重,特別是需要使用多個properties或你的資料本身有層次結構。為了控制和校驗你的應用配置,Spring Boot提供一個允許強型別beans的替代方法來使用properties。
示例:

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings { 
  private String username; 
  private InetAddress remoteAddress; 
// ... getters and setters
}

當@EnableConfigurationProperties註解應用到你的@Configuration時,任何被@ConfigurationProperties註解的beans將自動被Environment屬性配置。這種風格的配置特別適合與SpringApplication的外部YAML配置進行配合使用。

為了使用@ConfigurationProperties beans,你可以使用與其他任何bean相同的方式注入它們。

@Service
public class MyService {
   @Autowired 
    private ConnectionSettings connection; 
//... 
    @PostConstruct
     public void openConnection() { 
            Server server = new Server();
             this.connection.configure(server);
     }
}

你可以通過在@EnableConfigurationProperties註解中直接簡單的列出屬性類來快捷的註冊@ConfigurationProperties bean的定義。

@Configuration
@EnableConfigurationProperties(ConnectionSettings.class)
public class MyConfiguration {
}

第三方配置
正如使用@ConfigurationProperties註解一個類,你也可以在@Bean方法上使用它。當你需要繫結屬性到不受你控制的第三方元件時,這種方式非常有用。
為了從Environment屬性配置一個bean,將@ConfigurationProperties新增到它的bean註冊過程:

@ConfigurationProperties(prefix = "foo")
@Bean
public FooComponent fooComponent() { 
  ...
 }

和上面ConnectionSettings的示例方式相同,任何以foo為字首的屬性定義都會被對映到FooComponent上。

@ConfigurationProperties校驗
Spring Boot將嘗試校驗外部的配置,預設使用JSR-303(如果在classpath路徑中)。你可以輕鬆的為你的@ConfigurationProperties類新增JSR-303 javax.validation約束註解:

@Component
@ConfigurationProperties(prefix="connection")
public class ConnectionSettings {
 @NotNull private InetAddress remoteAddress; 
}

Profiles
Spring Profiles提供了一種隔離應用程式配置的方式,並讓這些配置只能在特定的環境下生效。任何@Component或@Configuration都能被@Profile標記,從而限制載入它的時機。
@Configuration
@Profile("production")
public class ProductionConfiguration {
}

以正常的Spring方式,你可以使用一個spring.profiles.active的Environment屬性來指定哪個配置生效。你可以使用平常的任何方式來指定該屬性,例如,可以將它包含到你的application.properties中:
spring.profiles.active=dev,hsqldb

或使用命令列開關:
--spring.profiles.active=dev,hsqldb

新增啟用的配置(profiles)
spring.profiles.active屬性和其他屬性一樣都遵循相同的排列規則,最高的PropertySource獲勝。也就是說,你可以在application.properties中指定生效的配置,然後使用命令列開關替換它們。

以程式設計方式設定profiles
在應用執行前,你可以通過呼叫SpringApplication.setAdditionalProfiles(…)方法,以程式設計的方式設定生效的配置。使用Spring的ConfigurableEnvironment介面激動配置也是可行的。

日誌
Spring Boot內部日誌系統使用的是Commons Logging,但開放底層的日誌實現。預設為會Java Util Logging, Log4J, Log4J2Logback提供配置。每種情況下都會預先配置使用控制檯輸出,也可以使用可選的檔案輸出。
預設情況下,如果你使用'Starter POMs',那麼就會使用Logback記錄日誌。為了確保那些使用Java Util Logging, Commons Logging, Log4J或SLF4J的依賴庫能夠正常工作,正確的Logback路由也被包含進來。
注:如果上面的列表看起來令人困惑,不要擔心,Java有很多可用的日誌框架。通常,你不需要改變日誌依賴,Spring Boot預設的就能很好的工作。

檔案輸出
預設情況下,Spring Boot只會將日誌記錄到控制檯而不會寫進日誌檔案。如果除了輸出到控制檯你還想寫入到日誌檔案,那你需要設定logging.file或logging.path屬性
日誌檔案每達到10M就會被輪換(分割),和控制檯一樣,預設記錄ERROR, WARN和INFO級別的資訊。

日誌級別
所有支援的日誌系統在Spring的Environment(例如在application.properties裡)都有通過'logging.level.*=LEVEL'('LEVEL'是TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF中的一個)設定的日誌級別。
示例:application.properties
logging.level.org.springframework.web: DEBUGlogging.level.org.hibernate: ERROR

自定義日誌配置
通過將適當的庫新增到classpath,可以啟用各種日誌系統。然後在classpath的根目錄(root)或通過Spring Environment的logging.config屬性指定的位置提供一個合適的配置檔案來達到進一步的定製(注意由於日誌是在ApplicationContext被建立之前初始化的,所以不可能在Spring的@Configuration檔案中,通過@PropertySources控制日誌。系統屬性和平常的Spring Boot外部配置檔案能正常工作)。
根據你的日誌系統,下面的檔案會被載入:
日誌系統
定製

Logback
logback.xml

Log4j
log4j.properties或log4j.xml

Log4j2
log4j2.xml

JDK (Java Util Logging)
logging.properties

相關文章