日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

今天了不起帶大家研究了一個 SpringBoot? 的外部化配置,并且通過實際的一個 case 跟蹤代碼的調用鏈來給大家測試了一下,雖然說這個知識點我們經常都在使用,但是沒看到底層源碼的時候我們并不知道這樣的一個功能底層是怎樣的復雜的。

作為 JAVA? 程序員,相信大家都知道,我們日常的 SpringBoot? 項目會有一個配置文件 Application.properties 文件。

里面會配置很多參數,例如服務的端口等,這些都只是默認值,在不改變配置文件里面內容的情況下,我們可以通過在部署的時候,傳遞一個相應的參數來替換默認的參數。

那么問題來了,你有想過為什么可以這樣嗎?為什么 SpringBoot 部署時傳遞的啟動配置會生效,而配置文件中的配置就不生效了呢?或者說這兩者的優先級是什么樣子的呢?

外部化配置

要解釋上面的問題,我們就需要知道 SpringBoot 到底支持哪些配置形式,以及這些配置方式的優先級是什么樣子的,只有搞清楚了這個,才能真正的解決配置的優先級問題。

在 SpringBoot 的官方文檔中我們可以看到這么一段描述

圖片

用了不起我拙劣的英語翻譯一下,大概的意思就是:Spring Boot? 提供了將配置文件外部化的功能,這樣您就可以在不同環境下使用相同的應用程序代碼。您可以使用 properties? 文件、YAML 文件、環境變量以及命令行參數來外部化配置文件。

通過 @Value? 注解,屬性值可以直接注入到 beans? 中,通過 Environment abstraction?(環境映射)可以訪問其他位置,或者使用 @ConfigurationProperties 綁定結構化對象。

有哪些外部配置

既然上面提到了 SpringBoot? 提供了外部化配置,那么 SpringBoot 提供了哪些配置呢?依然是通過官方文檔,我們可以看到有如下配置列表

圖片

從上圖可以看到 SpringBoot 總共內置了 17 種外部化配置方法,而且這 17 種的優先級是從上到下依次優先的。這些方式中我們常用的有 4 命令行方法,9 Java 系統環境變量,10 操作系統環境變量,以及 12 到 15 到配置文件的形式。

通過上面的順序我們就可以解釋為什么我們通過命令行配置的參數會生效,而配置文件中的默認值就會忽略了,從而達到了覆蓋配置的目的。

PropertySource

上面的文檔中也提到了,SpringBoot? 主要是通過 PropertySource? 機制來實現多樣屬性源的,SpringBoot? 的 PropertySource? 是一種機制,用于加載和解析配置屬性,可以從多種來源獲取這些屬性,例如文件、系統環境變量、JVM? 系統屬性和命令行參數等。PropertySource? 是 Spring 框架中的一個抽象接口,它定義了如何讀取屬性源的方法。

圖片

通過 SpringBoot? 的代碼,我們可以看到,org.springframework.core.env.PropertySource? 是一個抽象類,實現在子類有很多,我們上面提到的命令行 PropertySource? 是 org.springframework.core.env.CommandLinePropertySource。整體的類圖如下,涵蓋的內容還是很多的,感興趣的小伙伴可以好好研究一番。

圖片

另外在 SpringBoot? 中,我們還可以使用 @PropertySource 注解來自定義指定要加載的屬性文件。例如,可以在應用程序的主類上添加以下注解:

@SpringBootApplication
@PropertySource("classpath:customer.properties")
public class CustomerProperties {
   // ...
}

這將告訴 SpringBoot? 在 classpath? 下查找名為 customer.properties? 的文件,并將其加載為屬性源。然后,可以使用 @Value?注解將屬性值注入到 bean 中,如下所示:

@Service
public class MyService {
   @Value("${my.property}")
   private String myProperty;
   // ...
}

這里的 ${my.property}? 是從 customer.properties? 文件中獲取的屬性值。如果找不到該屬性,那么 SpringBoot 將使用默認值,這里因為是自定義的屬性,是沒有默認值的,就會報錯,項目無法啟動。

具體實現是,SpringBoot? 在啟動時會自動加載和解析所有的 PropertySource?,包括默認的 PropertySource? 和自定義的PropertySource?。這些屬性值被存儲在 Spring? 環境中,可以通過 Spring? 的 Environment? 對象訪問。當屬性被注入到 bean? 中時, Spring? 會查找 Environment 對象并嘗試解析屬性的值。

總之,SpringBoot? 的 PropertySource? 提供了一種簡單的方法來加載和解析應用程序的配置屬性,這些屬性可以從多個來源獲取。它通過將屬性值存儲在 Spring 環境中,使其易于在應用程序的不同部分中使用。

調試

為了驗證上面說的命令行的參數配置要優先于配置文件,我們創建一個 SpringBoot 項目,并且在 application.properties? 文件中配置一個參數 name=JavaGeekTech?,而在 IDEA 啟動窗口中配置 name=JAVA_JIKEJUSHU,分別如下所示

圖片

圖片

在寫一個簡單的 HelloController? 類,并且通過 @Value? 注解注入 name? 屬性,接下來我們就需要調試看下,SpringBoot?是如何將 name? 屬性賦值的。通過驗證 name? 會被賦值成 JAVA_JIKEJISHU? 而不是 JavaGeekTech。

package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

  @Value("${name}")
  private String name;

  @GetMapping(value = "/hello")
  public String hello() {
    return helloService.sayHello(name);
  }

}

接著我們啟動 debug?,因為我們是基于 SpringBoot? 的,屬性的賦值是在創建 bean? 的時候,從 createBean?,到 doCreateBean?,再到 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean?,因為每個 bean? 都會經過很多 PostProcessor? 的處理,屬性賦值的 PostProcessor? 是 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties

圖片

里面的 metadata.inject? 會調用到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject?,再到 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue,

org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency,

org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency,

org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue,

org.springframework.core.env.AbstractPropertyResolver#resolveRequiredPlaceholders,

org.springframework.core.env.PropertySourcesPropertyResolver#getPropertyAsRawString,

org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class<T>, boolean)

整體調用鏈還是挺長的,不過只要跟著思路,在配合斷點,還是可以看看看出來的。

圖片

在 getProperty? 方法中,我們可以看到如下的邏輯,根據 key? 獲取到的 value? 值為JAVA_JIKEJISHU。

圖片

繼續跟蹤 getProperty? 方法,我們可以看到這個方法 org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource#findConfigurationProperty(org.springframework.boot.context.properties.source.ConfigurationPropertyName),

圖片

其中的 getSource() 中就有我們配置的兩個屬性源的數據,如下所示

圖片

根據代碼邏輯,我們也可以看到,在迭代的時候,如果找到了一個就直接返回了,所以得到的結果是JAVA_JIKEJISHU。

總結

今天了不起帶大家研究了一個 SpringBoot? 的外部化配置,并且通過實際的一個 case 跟蹤代碼的調用鏈來給大家測試了一下,雖然說這個知識點我們經常都在使用,但是沒看到底層源碼的時候我們并不知道這樣的一個功能底層是怎樣的復雜的。

這里還是要敬佩一下 SpringBoot 的開發者,同時也建議大家,在日常的開發中我們需要多看看底層的源碼,通過不斷的看源碼,我們能更好的理解特性的實現原理,從而加強我們自身的能力。

分享到:
標簽:SpringBoot
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定