日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Environment全解析

發布時間:2023/12/16 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Environment全解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、前言
  • 二、Spring Environment簡介
    • 2.1 env 的 profiles 不同環境配置分組
      • 2.1.1 env 的 profiles 基本使用
      • 2.1.2 配置環境三地方:啟動類、application.properties配置文件、Run/Debug Configuration
      • 2.1.3 setActiveProfiles()參數是數組,可以設置多個
    • 2.2 env 的 properties:存放屬性的環境或文件信息 @Value("{xxx}")
      • 2.2.1 用@Value取出普通配置
      • 2.2.2 用@Value取出系統配置,如java.version
      • 2.2.3 用Environment實例bean取出系統配置,如java.version
      • 2.2.4 為什么可以用Environment實例bean取出java.version這種系統配置
  • 三、Spring Environment源碼解析
    • 3.1 從run()方法開始
    • 3.2 第一子方法:新建或獲取環境getOrCreateEnvironment()方法
      • 3.2.1 AbstractEnvironment類
      • 3.2.2 StandardEnvironment類
      • 3.2.3 StandardServletEnvironment類
      • 3.2.4 MutablePropertySources類
    • 3.3 第二子方法:準備環境configureEnvironment()
      • 3.3.1 SpringApplication類中的configureEnvironment()
      • 3.3.2 解析properties屬性:SpringApplication類的configurePropertySources()
      • 3.3.3 SpringApplication類的configureProfiles()
    • 3.4 第三子方法:開始加載springboot的配置listeners.environmentPrepared(environment)
      • 3.4.1 listeners.environmentPrepared(environment)
      • 3.4.2 onApplicationEnvironmentPreparedEvent()
      • RandomValuePropertySource類
  • 四、尾聲

一、前言

二、Spring Environment簡介

2.1 env 的 profiles 不同環境配置分組

2.1.1 env 的 profiles 基本使用

首先,我們知道,整個spring應用運行的環境信息:profiles + properties

先看profiles,在代碼中的配置為:

spring.profiles.active=prd/test/dev...

profiles作用:對bean邏輯分組

先定義一個ProfileService類,包含一個私有屬性profile,如下:

新建一個ProfileConfiguration配置類,將新建的ProfileService類的實例bean定義在這里,等到springboot項目啟動的時候,將可以將ProfileService實例bean裝載的ioc容器中去了,整個如下:

在ProfileConfiguraiton配置類的各個bean上,加上@Profile注解,模擬設置兩種不同環境下的bean,聲明不同環境的bean

金手指:使用@Profile對不同的bean邏輯分布 xml 和注解都可以

現在我們不使用SpringbootApplication類啟動了,直接寫一個main方法啟動即可,如下,在啟動之前,在Environment里面設置profile,setActiveProfile()蠶食是數組,可以設置躲著,這里測試之用,先設置“prd”,最后context.getBean可以取出ioc容器中的bean,打印到控制臺就好。

一般來說,getBean和@Autowired兩種方式都可以取出ioc容器中的bean.

運行springboot工程,如果打印出來的ioc容器中的bean,prd環境中的bean,就可以了。

至此,對于env 的 profiles設置成功。

2.1.2 配置環境三地方:啟動類、application.properties配置文件、Run/Debug Configuration

我們能夠配置環境的地方不止一個,不僅像上面在啟動類中可以配置,還是在application.properties配置文件中配置,還可以在 Run/Debug Configuration 中配置

2.1.3 setActiveProfiles()參數是數組,可以設置多個

setActiveProfiles()參數是數組,可以設置多個,如下:

至此,對于 env 的 profiles 的講解完成。

2.2 env 的 properties:存放屬性的環境或文件信息 @Value(“{xxx}”)

2.2.1 用@Value取出普通配置

新建一個UserController類,打印出application.properties配置文件中的env屬性即可,如下:

application.properties配置文件中的env屬性為“hello world”,配置如下:

運行成功,取出來了,如下:

所以,我們看到,properties存放屬性的環境或文件信息。

2.2.2 用@Value取出系統配置,如java.version

實際上,@Value注解還可以訪問到系統環境變量的信息,如java.version,取出當前jdk版本,試一試:

看,打印出來了。

2.2.3 用Environment實例bean取出系統配置,如java.version

我們用@Autowired取出spring ioc容器中的Environment實例bean,然后直接用Environment實例bean(不用@Value)來取,如下:

取出spring ioc容器中的實例bean有兩種方式,getBean方法 或 @Autowired注解。


看,也打印出來了。

2.2.4 為什么可以用Environment實例bean取出java.version這種系統配置

那問題來了,為什么可以用Environment取出java.version這種系統配置呢?一起來看看Spring中的Environment接口,如下:


理由很簡單,因為Environment實例bean已經在ioc容器中了,所以要取出系統配置java.version很簡單,因為這個系統配置java.version就是寫在Environment的實例bean里面的。

其他的,如BeanFactory、ApplicationContext實例bean也是在ioc容器里的,還實現了Environment接口,如下:

其實,作為Spring容器的內置接口,Environment中的配置有很多來源:系統的環境變量(如上面提到的java.version)、系統變量 System.propreties等。

大體上來說,Environment中的配置來源大體包括兩大類:系統屬性源 + springboot屬性源,如下:

這個圖很重要,本文接下來就講這個圖,講Environment是如何處理 “系統屬性源” 和 “springboot屬性源” 的。

金手指:springboot啟動類自動掃描所在包及其子包下spring+springmvc注解,但是不會掃描ibatis的@Mapper注解

三、Spring Environment源碼解析

先找到Environment接口

3.1 從run()方法開始

找打springboot工程的run方法,如下:

不斷ctrl+左鍵,進入到真正有意義的run方法中,如下:

在這個真正有意義的run方法中,使用prepareEnvironment方法得到一個env實例,然后將這個實例設置到context中去,如下:

金手指:源碼中,spring或springboot初始化加載時,env 在 context 之前,合乎常理。這里看的springboot的源碼,其實spring的源碼也一樣。

選擇prepareEnvironment方法

進入prepareEnvironment方法,先使用getOrCreateEnvironment方法新建或獲得一個環境變量,然后將這個env實例放到configureEnvironment方法中去完成相關配置,最后通過listeners.environmentPrepared方法,將env實例交給監聽事件,如下:

接下來看一下SpringApplication類prepareEnvironment()方法的三個子方法。

3.2 第一子方法:新建或獲取環境getOrCreateEnvironment()方法

可以看到,如果類變量env不為null,就直接返回,如果為null,就根據類變量webApplicationType的實例類型,new一個Env實現類實例返回。

所以說,getOrCreateEnvironment()方法中涉及三個類:StandardEnvironment StandardServletEnvironment StandardReactiveWebEnvironment,這三個類都繼承AbstractEnvironment,AbstractEnvironment又繼承于Environment接口,關系如下:

先看AbstractEnvironment類

3.2.1 AbstractEnvironment類

在AbstractEnvironment的構造方法中,調用一個自定義屬性源的方法,如下:

這個自定義屬性源的方法,在AbstractEnvironment中是空實現,只能看它的子類了。

值得注意的是,對于AbstractEnvironment構造函數,

public AbstractEnvironment() {customizePropertySources(this.propertySources); }

這是一個很優美的設計,可以將customizePropertySources的實現交給子類的處理,是模板模式。

AbstractEnvironment類就到這里的,我們看其子類StandardEnvironment類。

3.2.2 StandardEnvironment類

既然AbstractEnvironment類的customizePropertySources()是空實現,看看其子類StandardEnvironment類。

StandardEnvironment類只有一個customizePropertySources方法實現,也看完了,且看StandardServletEnvironment類。

3.2.3 StandardServletEnvironment類

直接轉到customizePropertySources方法實現,如下:

關于customizePropertySources()方法,customizePropertySources()接收可變參數源作為輸入。

該方法在AbstractEnvironment類中為空實現,在StandardEnvironment類中為將系統配置和系統環境變量放到env中,在StandardServletEnvironment類中為servlet配置屬性、servlet上下文屬性、JNDI屬性,并調用StandardEnvironment的customizePropertySources()方法,所以同時有了servlet配置屬性、servlet上下文屬性、JNDI屬性、系統配置和系統環境變量五種屬性。

源碼設計的優美之處1:StandardServletEnvironment類中的customizePropertySources()方法調用了StandardEnvironment的customizePropertySources()方法,因為是它里面將系統配置和系統環境變量放到env中;StandardEnvironment的customizePropertySources()方法沒有調用AbstractEnvironment的customizePropertySources()方法,因為是它里面將里面方法體為空,不需要調用;這體現了源碼的優美。

源碼設計優美之處2:

public AbstractEnvironment() {customizePropertySources(this.propertySources); }

AbstractEnvironment類的構造方法中調用customizePropertySources(),以后customizePropertySources()一層層被重寫,反正調用最后子類的,具體環境生產具體對象,這體現了源碼設計優美。

3.2.4 MutablePropertySources類

customizePropertySources()接收可變參數源作為輸入,實際上就是一個MutablePropertySources類對象,讓我們來看一下這個MutablePropertySources類,其定義如下:


讓我們看看StandardServletEnvironment添加的三個屬性是什么


在我們使用spring+springmvc的時候,需要配置一個web.xml,web.xml里面需要配置兩個標簽< context-params>< /context-params>和< init-params>< /init-params>,當時我們只是這樣這樣使用,實際上,web.xml中這兩個標簽作為屬性配置到env里面去了。

public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = “servletContextInitParams”; 就是 < context-params>< /context-params>
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = “servletConfigInitParams”; 就是 < init-params>< /init-params>

我們知道,StandardEnvironment:系統變量+系統環境變量

/** System environment property source name: {@value}. */ public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";/** JVM system properties property source name: {@value}. */ public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

上面,前者表示系統環境變量,所以我們剛才使用@Value(“${java.version}”)訪問到了系統環境變量,后者表示系統屬性。

我們現在完善圖,一圖小結源碼結構:

最后一個問題,配置順序,優先級如何確定?

回答:不管是StandardEnvironment類和還是StandardServletEnvironment類,都是使用addLast添加屬性的,所有屬性的順序就是源代碼從上到下。


好了,ConfigurableEnvironment environment = getOrCreateEnvironment(); 這行代碼完了,回到SpringApplication類,看configureEnvironment(environment, applicationArguments.getSourceArgs());這行代碼。

3.3 第二子方法:準備環境configureEnvironment()

3.3.1 SpringApplication類中的configureEnvironment()

configureEnvironment()是在新建/獲取env實例后執行的,如下:

在configureEnvironment()方法中,第一個實參就是ConfigurableEnvironment類對象,就是env對象,剛剛看過了,第二個參數是一個DefaultApplicationArguments類對象.getSourceArgs(),它是在源碼中new出來的,如下(簡答了解就好,只要知道命令行參數也是可以被識別的就好):

進入到重點,看到configureEnvironment方法,如下:

類變量addConversionService 默認為true,

private boolean addConversionService = true;

它表示統一類型轉換,當其為true,執行if代碼塊,得到一個conversionService,并設置到Spring框架的env中去。

接著往下看,可以看到,configureEnvironment方法先后執行了兩個方法,就是 configurePropertySources(environment, args); 和 configureProfiles(environment, args); ,就是整個spring應用運行的環境信息:profiles + properties,就是在這兩個方法里面解析出來的。

3.3.2 解析properties屬性:SpringApplication類的configurePropertySources()

看到configurePropertySources()方法,這個方法解析開發者配置的所有properties屬性。

先獲得env實例中的屬性源,如果默認屬性不為空,將其添加到sources中,addLast就是在list末尾添加,如果啟動的時候,命令行中也傳入了參數,也添加進來,沒傳入也沒關系。

小結,繼續更新env圖

env中添加命令行參數很好懂,但是默認參數defaultproperties是什么?從哪里來的?先看這個defaultproperties的定義,它在SpringApplication類中,默認是一個map結構,定義如下:


值得注意的是,這個defaultproperties僅僅在springboot才有的,spring是沒有的。

關于這個defaultproperties,可以自己設置 setDefautProperties,嘗試一下,如下面springboot啟動類中。

運行啟動起來,看,在源碼中斷點,真的走到了這個地方,取出defaultProperties變量,看到自己手動添加的屬性。


好了,configurePropertySources()方法完成了,下面看configureProfiles()方法。

3.3.3 SpringApplication類的configureProfiles()

先打開configureProfiles()的源碼,看到就是根據類變量additionalProfiles新建一個LinkedHashSet,然后將env變量中的profiles屬性都放到這個新建的set中,最后將這個set變為字符串又放到env中去。

好了,我們進入到setActiveProfiles()方法,如下:

setActiveProfiles()設置env中的profile


關于configureProfiles方法,看起來好像沒什么意義?將env中的profiles放到set中,然后又將set放到env中。

但是,請注意,這里這個AbstractEnvironment類中的setActiveProfiles()方法接收String類型可變數組,同時將字符串類型的profile放到一個Set類型的集合activeProfiles中,因為set集合,可以配置多個,但是不能字符串重名。

實際上,這個方法我們一開始就用到,在env中設置profile,接受一個數組,包含兩個字符串 “prd” 和 “env” ,保證既是數組又不重名。

其實,在application.properties文件中設置和在Run/Debug Configurations中設置都是一個道理,底層都是調用這個方法setActiveProfiles()。

3.4 第三子方法:開始加載springboot的配置listeners.environmentPrepared(environment)

3.4.1 listeners.environmentPrepared(environment)

這里就是一個監聽邏輯了,復習一下,在Spring中,通用的監聽邏輯是:自定義一個事件類,事件發布者發布一個自定義事件,時間接受者listener接收自定義事件及其子類事件。

進入listeners.environmentPrepared(environment);

再進入environmentPrepared方法,如下:
看一下廣播事件的具體邏輯,如下:

再進去invokeListener方法,如下:

這個listener.onApplicationContext(event)就是表示監聽者監聽到事件觸發后要完成的相應邏輯。

好了,我們重溫一下這個調用關系,如下:

好了,我們來看看這個listener監聽到事件發生后的具體操作,進入onApplicationEvent方法,但是這種方法有很多個,如下:

任意找一個,這里找ConfigFileApplicationListener類,可以看到對于通過event類型,執行了不同操作,如下:

對于onApplicationEvent的第二個方法onApplicationPreparedEvent,如下:

EnvironmentPostProcessor有很多實現類,如下:

找到幾個EnvironmentPostProcessor的實現類,這里選擇SpringApplicationJsonEnvironmentPostProcessor類,這個類中,實現了兩個接口,EnvironmentPostProcessor接口是擴展,Ordered接口是排序,如下:

3.4.2 onApplicationEnvironmentPreparedEvent()

關于onApplicationEvent的第一個方法onApplicationEnvironmentPreparedEvent()方法,如下:

再次進入到postProcessEnvironment方法,如下:

好了,整理一下調用關系,如下:

繼續進入到addPropertySources方法,如下:

我們注意到,在ConfigFileApplicationListener類中,里面有三個重要的類變量,DEFAULT_PROPERTIES默認屬性,DEFAULT_SEARCH_LOCATIONS默認掃描位置(開發者的文件應該存放的路徑位置),DEFAULT_NAMES默認名稱(開發者的文件的默認名稱),如下:

在addPropertySources方法中,三步走,先添加,然后新建一個Loader,最后執行load方法,如下:

第一步,先添加,進入RandomValuePropertySource.addToEnvironment方法,如下:

RandomValuePropertySource類

我們先來認識一下這個RandomValuePropertySource類,類上的注釋就告訴我們怎么配置,如下:

類中有兩個常量,默認隨機屬性名稱為random,前綴為random.,如下:

getProperty方法:當實參name前綴不為random.,直接返回為null;當實參name的前綴為random.,調用getRandomValue方法返回一個隨機值。

getRandomValue方法:根據實參name取前綴長度的字符串匹配,返回具體的隨機值,


好了,我們按照RandomValuePropertySource類的配置,自動動手試一試,配置一個name為randomLong,值為random.long,底層是由RandomValuePropertySource類的getRandomValue方法生成的,配置如下:

主代碼中通過@Value或者Environment的bean實例取出屬性值,這里使用Environment的bean實例,如下:

運行,真的取出來了

刷新一次又變了,每一次都會生成一個隨機long,哈哈


好了,玩夠了,回到ConfigFileApplicationListener類的addPropertySources方法:添加,新建Loader,執行load方法。

進入到addToEnvironment()方法,這里的addAfter就是在后面添加隨機屬性。

env如圖:

回到ConfigFileApplicationListener類,查看new Loader(environment, resourceLoader).load();

Loader是資源文件加載器


這個Loader類的構造方法就是設置四個類屬性,如下:

看到第四句,如下:

this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());

先看到這個SpringFactoriesLoader.loadFactories方法,這個方法就是加載spring.factories配置文件中的數據,

再看PropertySourceLoader接口本身,這個接口僅包含兩個方法,獲取文件后綴名方法和加載方法。

看到load方法

進入FilteredPropertySource.apply()方法,如下:

FilteredPropertySource.javastatic void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties,Consumer<PropertySource<?>> operation) {// 取出env實例bean中的屬性源,放到可變屬性源propertySources中MutablePropertySources propertySources = environment.getPropertySources(); // 在可變屬性源propertySources中,取得指定propertySourceNamePropertySource<?> original = propertySources.get(propertySourceName);if (original == null) {operation.accept(null); // 如果取出為null,執行operation.acceptreturn;}// 如果取出不為null,即當前存在propertySourceName,新建一個過濾屬性源替換當前的propertySourceNamepropertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties));try {operation.accept(original);}finally {propertySources.replace(propertySourceName, original);} }



回到load方法,看到initializeProfiles方法

進入到initializeProfiles方法,該方法初始化profiles屬性,邏輯如下:


進入到getOtherActiveProfiles方法,如下:

經歷了Loader構造函數,有了Loader對象,看一下load方法,如下:


在進入load方法,如下:


debug執行,如下:


進入到loadForFileExtension方法,如下:




四、尾聲

Spring Environment全解析,完成了。

天天打碼,天天進步!!

總結

以上是生活随笔為你收集整理的Spring Environment全解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。