1. 程式人生 > 實用技巧 >SpringBoot【二】 SpringBoot 配置

SpringBoot【二】 SpringBoot 配置

SpringBoot 配置

配置檔案

SpringBoot 使用一個全域性的配置檔案,配置檔名稱是固定的,作用是修改 SpringBoot 自動配置的預設值(底層自動配置的值),有兩種方式可以使用:

  • application.properties

    • 語法結構 :key=value
  • application.yaml【推薦使用】

    • 語法結構 :key:空格value

比如可以在配置檔案中修改 Tomcat 預設啟動的埠號

server.port=8081

yaml

YAML是 "YAML Ain't a Markup Language" (YAML不是一種標記語言)的遞迴縮寫。在開發的這種語言時,YAML 的意思其實是:"Yet Another Markup Language"(仍是一種標記語言)

這種語言以資料作為中心,而不是以標記語言為重點!

1、yaml 和 xml

以前的配置檔案,大多數都是使用xml來配置

  • 傳統 xml 配置:

    <server>    
        <port>8081<port>
    </server>
    
  • yaml 配置:

    server:  
    	prot: 8080
    

2、基礎語法

語法要求嚴格

  1. 空格不能省略;

  2. 以縮排來控制層級關係,只要是左邊對齊的一列資料都是同一個層級的;

  3. 屬性和值的大小寫都是十分敏感的。

  • 字面量:普通的值 [ 數字,布林值,字串 ]

    字面量直接寫在後面就可以,字串預設不用加上雙引號或者單引號

    k: v
    

    注意:

    • “ ” 雙引號,不會轉義字串裡面的特殊字元,特殊字元會作為本身想表示的意思

      比如:name: "zhang \n san" 輸出 :zhang 換行 san

    • '' 單引號,會轉義特殊字元,特殊字元最終會變成和普通字元一樣輸出

      比如 :name: ‘zhang \n san’ 輸出 :zhang \n san

  • 物件、Map(鍵值對)

    #物件、Map格式
    k:     
    	v1:    
    	v2:
    

    在下一行來寫物件的屬性和值的關係,注意縮排

    student:    
    	name: zhangsan   
    	age: 3
    	
    # 行內寫法
    student: {name: zhangsan,age: 3}
    
  • 陣列( List、set )

    用 - 值表示陣列中的一個元素,比如:

    pets: 
    	- cat 
    	- dog 
    	- pig
    	
    # 行內寫法
    pets: [cat,dog,pig]
    

注入配置檔案

1、yaml 配置檔案注入

yaml 檔案強大的地方在於,它可以給我們的實體類直接注入匹配值。

  1. 在 springboot 專案的 resources 目錄下新建一個檔案 application.yml

  2. 編寫一個實體類 Dog

  3. 原來 Spring 中給 bean 注入屬性值,通過 @Value

    @Component //註冊bean到容器中
    public class Dog {
        @Value("旺財")
        private String name;
        @Value("3")
        private int age;
        ...//有參無參構造、get、set方法、toString()方法  
    }
    
  4. SpringBoot 的測試類下注入 Dog 物件並輸出

    @SpringBootTest
    class Springboot02ConfigApplicationTests {
    
    	@Autowired // 自動注入
    	private Dog dog;
    
       @Test
       void contextLoads() {
          System.out.println(dog); // 輸出結果:Dog{name=旺財, age=3}
       }
    }
    
  5. 編寫一個複雜一點的實體類:Person 類

    @Component //註冊 bean 到容器中
    public class Person {
        
        private String name;
        private int age;
        private boolean happy;
        private Date birth;
        private Map<String,Object> maps;
        private List<Object> lists;
        private Dog dog;
    	... //有參無參構造、get、set方法、toString()方法  
    }
    
  6. 使用 yaml 配置的方式進行注入,編寫 application.yml

    person:
      name: zhangsan
      age: 3
      happy: true
      birth: 2020/02/02
      maps: {k1: v1,k2: v2}
      lists:
        - code
        - music
        - dance
      dog:
        name: wangcai
        age: 3
    
  7. 注入到 Person 類中

    @Component
    @ConfigurationProperties(prefix = "person")
    public class Person {
    	... // 不變
    }
    

    @ConfigurationProperties 作用:

    將配置檔案中配置的每一個屬性的值,對映到這個元件中,告訴 SpringBoot 將本類中的所有屬性和配置檔案中相關的配置進行繫結,其中引數 prefix = “person” 表示將與配置檔案中的 person 下面的所有屬性一一對應。

  8. 標註 @ConfigurationProperties 註解後,IDEA 提示報紅(但是不影響程式執行),SpringBoot 配置註解處理器沒有找到,檢視文件,找到一個依賴

    <!-- 匯入配置檔案處理器,配置檔案進行繫結就會有提示,需要重啟 -->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-configuration-processor</artifactId>
       <optional>true</optional>
    </dependency>
    
  9. 測試類中測試

    @SpringBootTest
    class Springboot02ConfigApplicationTests {
    
       @Autowired
       private Person person;
    
       @Test
       void contextLoads() {
          System.out.println(person);
       }
    
  10. 輸出 Person 物件,屬性值和 yaml 注入的相同,說明所有值全部注入成功

    Person{name='zhangsan', age=3, happy=true,birth=Sun Feb 02 00:00:00 GMT+08:00 2020,maps={k1=v1, k2=v2}, lists=[code, music, dance],dog=Dog{name='wangcai', age=3}}
    

注意:當配置檔案的 key 值和屬性的值設定的不一樣時,結果輸出為 null,注入失敗。

配置檔案佔位符:配置檔案可以編寫佔位符生成隨機數

person:
 name: zhangsan${random.uuid} # 隨機uuid
 age: ${random.int}  # 隨機int
 happy: true
 birth: 2020/02/02
 maps: {k1: v1,k2: v2}
 lists:
   - code
   - music
   - dance
 dog:
   name: ${person.hello:other}_wangcai
   age: 3

2、properties 配置檔案注入(載入指定的配置檔案)

properties 配置檔案在寫中文的時候,會有亂碼,需要去 IDEA 中(settings --> FileEncodings)設定編碼格式為UTF-8。

@PropertySource :載入指定的配置檔案

@configurationProperties:預設從全域性配置檔案中獲取值

  1. 在 resources 目錄下新建一個 person.properties 檔案

    name=lisi
    
  2. 然後在 Person 類中指定載入 person.properties 檔案

    @Component
    // 載入指定的配置檔案
    @PropertySource(value = "classpath:application.properties")
    public class Person {	//直接使用@value  
        
        // @Value("男")  // 字面量 
        // @Value("#{9*2}")  // #{SPEL} Spring表示式
        @Value("${name}") // spring EL表示式取出配置檔案的值
        private String name;
        private int age;
        ...
    }
    
  3. 測試輸出,Person 物件中只有 name 屬性被賦值為 lisi,其他都為 null,說明配置檔案繫結成功。

    Person{name='lisi', age=null, happy=null, birth=null, maps=null, lists=null, dog=null}
    

注意:@Value 使用起來並不友好,因為我們需要為每個屬性單獨註解賦值,比較麻煩

3、兩種方式對比

  1. @ConfigurationProperties 只需要寫一次即可, @Value 則需要每個欄位都新增

  2. 鬆散繫結:比如 yml 中寫的 last-name,和實體類中的 lastName 一樣, - 後面跟著的字母預設是大寫的。

  3. JSR303 資料校驗 , 可以在欄位上增加一層過濾器驗證,可以保證資料的合法性

  4. 複雜型別封裝,yml 中可以封裝物件 , 使用 value 就不支援

結論:

  • 配置 yml 和配置 properties 都可以獲取到值 , 強烈推薦 yml;

  • 如果在某個業務中,只需要獲取配置檔案中的某個值,可以使用一下 @value;

  • 如果專門編寫了一個 JavaBean 來和配置檔案進行一一對映,使用 yaml 和 @configurationProperties。

JSR303 資料校驗

Springboot 中可以用 @validated 來校驗資料,如果資料異常則會統一丟擲異常,方便異常中心統一處理。使用資料校驗,可以保證資料的正確性。

在 Person 類中加入註解讓 name 只能支援 Email 格式;

@Component
@ConfigurationProperties(prefix = "person")
@Validated // 資料校驗
public class Person {

    @Email(message = "郵箱格式錯誤") //name必須是郵箱格式
    private String name;
	...
}

執行結果:default message [不是一個合法的電子郵件地址];

常見引數

@NotNull(message="名字不能為空")
private String userName;
@Max(value=120,message="年齡最大不能查過120")
private int age;
@Email(message="郵箱格式錯誤")
private String email;

空檢查
@Null       驗證物件是否為null
@NotNull    驗證物件是否不為null, 無法查檢長度為0的字串
@NotBlank   檢查約束字串是不是Null還有被Trim的長度是否大於0,只對字串,且會去掉前後空格.
@NotEmpty   檢查約束元素是否為NULL或者是EMPTY.    

Booelan檢查
@AssertTrue     驗證 Boolean 物件是否為 true  
@AssertFalse    驗證 Boolean 物件是否為 false      

長度檢查
@Size(min=, max=) 驗證物件(Array,Collection,Map,String)長度是否在給定的範圍之內  @Length(min=, max=) string is between min and max included.

日期檢查
@Past       驗證 Date 和 Calendar 物件是否在當前時間之前  
@Future     驗證 Date 和 Calendar 物件是否在當前時間之後  
@Pattern    驗證 String 物件是否符合正則表示式的規則
.......等等除此以外,我們還可以自定義一些資料校驗規則

多環境切換

profile 是 Spring 對不同環境提供不同配置功能的支援,可以通過啟用不同的環境版本,實現快速切換環境。

1、多配置檔案

在主配置檔案編寫的時候,檔名可以是 application-{profile}.properties/yml , 用來指定多個環境版本

例如:

  • application-test.properties 代表測試環境配置
  • application-dev.properties 代表開發環境配置

但是 Springboot 並不會直接啟動這些配置檔案,它預設使用 application.properties主配置檔案,需要通過一個配置來選擇需要啟用的環境:

#比如在配置檔案中指定使用dev環境,我們可以通過設定不同的埠號進行測試;
#我們啟動SpringBoot,就可以看到已經切換到dev下的配置了;
spring.profiles.active=dev

2、yaml 的多文件塊

和 properties 配置檔案中一樣,但是使用 yml 去實現不需要建立多個配置檔案,更加方便。

server:  
	port: 8081
	
#選擇要啟用那個環境塊
spring:  
	profiles:    
		active: prod
		
---
server:  
	port: 8083
spring:  
	profiles: dev #配置環境的名稱

---
server:  
	port: 8084
spring:  
	profiles: prod  #配置環境的名稱

注意:如果 yml 和 properties 同時都配置了埠,並且沒有啟用其他環境,預設會使用 properties 配置檔案。

配置檔案載入位置

官方文件說明,外部配置檔案位置有:

  1. file:./config/

  2. file:./

  3. classpath:./config/

  4. classpath:./

SpringBoot 啟動會掃描以下位置的 application.properties 或者 application.yml 檔案作為 SpringBoot 的預設配置檔案,優先順序由高到底,高優先順序的配置會覆蓋低優先順序的配置:

優先順序1:專案路徑(file)下的config資料夾配置檔案
優先順序2:專案路徑下配置檔案
優先順序3:資源路徑(classpath)下的config資料夾配置檔案
優先順序4:資源路徑下配置檔案

SpringBoot 會從這四個位置全部載入主配置檔案,互補配置。

指定位置載入配置檔案:

可以通過 spring.config.location 來改變預設的配置檔案位置,專案打包好以後,可以使用命令列引數的形式,啟動專案的時候來指定配置檔案的新位置,這種情況,一般是後期運維做的多,相同配置,外部指定的配置檔案優先順序最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.properties

自動配置原理

關注兩種檔案:

  • xxxAutoConfiguration:自動配置類,給容器中新增元件

  • xxxProperties :預設屬性類,封裝配置檔案中相關屬性,和配置檔案繫結,這樣就可以使用自定義的配置

原理分析:

HttpEncodingAutoConfiguration(Http編碼自動配置)和 ServerProperties 為例:

// 表示這是一個配置類,和以前編寫的配置檔案一樣,也可以給容器中新增元件
@Configuration(proxyBeanMethods = false)

// 自動配置屬性:ServerProperties
	//進入這個ServerProperties檢視,將配置檔案中對應的值和ServerProperties繫結起來;
    //並把ServerProperties加入到ioc容器中
@EnableConfigurationProperties(ServerProperties.class)

//Spring底層@Conditional註解
    //根據不同的條件判斷,如果滿足指定的條件,整個配置類裡面的配置就會生效;
    //這裡的意思就是判斷當前應用是否是web應用,如果是,當前配置類生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)

//判斷當前專案有沒有這個類CharacterEncodingFilter:SpringMVC中進行亂碼解決的過濾器
@ConditionalOnClass(CharacterEncodingFilter.class)

//判斷配置檔案中是否存在某個配置:server.servlet.encoding
    //如果不存在,判斷也是成立的
    //即使我們配置檔案中不配置 server.servlet.encoding=true,也是預設生效的;
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

   //它已經和SpringBoot的配置檔案映射了
   private final Encoding properties;

   //只有一個有參構造器的情況下,引數的值就會從容器中拿
   public HttpEncodingAutoConfiguration(ServerProperties properties) {
      this.properties = properties.getServlet().getEncoding();
   }

   //給容器中新增一個元件,這個元件的某些值需要從properties中獲取
   @Bean
   @ConditionalOnMissingBean
   public CharacterEncodingFilter characterEncodingFilter() {
      CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
      filter.setEncoding(this.properties.getCharset().name());
	filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		
      return filter;
   }
    ...
}
//從配置檔案中獲取指定的值和 bean 的屬性進行繫結
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    ...
}

總結:

根據當前不同的條件判斷,決定這個配置類是否生效;一旦這個配置類生效,這個配置類就會給容器中新增各種元件;這些元件的屬性是從對應的 properties 類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的;

所有在配置檔案中能配置的屬性都是在 xxxxProperties 類中封裝著,配置檔案能配置什麼就可以參照某個功能對應的這個屬性類。

精髓:

  1. SpringBoot 啟動會載入大量的自動配置類
  2. 看我們需要的功能有沒有在 SpringBoot 預設寫好的自動配置類當中;
  3. 再來看這個自動配置類中到底配置了哪些元件(只要需要用的元件存在其中,我們就不需要再手動配置)
  4. 給容器中自動配置類新增元件的時候,會從 properties 類中獲取某些屬性,我們只需要在配置檔案中指定這些屬性的值即可。

@Conditional

自動配置類必須在一定的條件下才能生效,主要原因是每個自動配置類上都添加了 @Conditional 的派生註解。

Spring 註解版原生的 @Conditional 作用:必須是 @Conditional 指定的條件成立,才給容器中新增元件,配置裡面的所有內容才生效。

問題:如何知道哪些自動配置類生效?

通過啟用 debug=true 屬性,來讓控制檯列印自動配置報告,這樣知道哪些自動配置類生效

#開啟springboot的除錯類
debug=true

輸出日誌中將配置類分為三種:

  • Positive matches:(自動配置類啟用的:正匹配)

  • Negative matches:(沒有啟動,沒有匹配成功的自動配置類:負匹配)

  • Unconditional classes: (沒有條件的類)