tensorflow 三種模型:ckpt、pb、pb-savemodel
配置檔案載入順序
SpringBoot也可以從以下位置載入配置; 優先順序從高到低;高優先順序的配置覆蓋低優先順序的配置,所有的配置會形成互補配置
-
命令列引數
所有的配置都可以在命令列上進行指定
java -jar xxx.jar --server.port=8087 --server.context-path=/abcCopy to clipboardErrorCopied
多個配置用空格分開; --配置項=值
-
來自java:comp/env的JNDI屬性 ⤴️
-
Java系統屬性(System.getProperties()) ⤴️
-
作業系統環境變數 ⤴️
-
RandomValuePropertySource配置的random.*屬性值 ⤴️
由jar包外向jar包內進行尋找;
優先載入帶profile
-
jar包外部的
application-{profile}.properties
或application.yml
(帶spring.profile)配置檔案 ⤴️ -
jar包內部的
application-{profile}.properties
或application.yml
(帶spring.profile)配置檔案 ⤴️
再來載入不帶profile
-
jar包外部的
application.properties
或application.yml
(不帶spring.profile)配置檔案 ⤴️ -
jar包內部的
application.properties
application.yml
(不帶spring.profile)配置檔案 ⤴️ -
@Configuration註解類上的@PropertySource ⤴️
-
通過SpringApplication.setDefaultProperties指定的預設屬性 ⤴️
所有支援的配置載入來源:
自動配置的執行流程
- @SpringBootApplication
1. @SpringBootApplication // 由啟動類的@SpringBootApplication開啟自動配置 2. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration //標至該類是一個配置類,與@Configuration作用一致 @EnableAutoConfiguration //啟動 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
從Spring3.0,@Configuration用於定義配置類,可替換xml配置檔案,被註解的類內部包含有一個或多個被@Bean註解的方法,這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進行掃描,並用於構建bean定義,初始化Spring容器。
注意:@Configuration註解的配置類有如下要求:
- @Configuration不可以是final型別;
- @Configuration不可以是匿名類;
- 巢狀的configuration必須是靜態類。
一、用@Configuration載入spring
1.1、@Configuration配置spring並啟動spring容器
1.2、@Configuration啟動容器+@Bean註冊Bean
1.3、@Configuration啟動容器+@Component註冊Bean
1.4、使用 AnnotationConfigApplicationContext 註冊 AppContext 類的兩種方法1.5、配置Web應用程式(web.xml中配置AnnotationConfigApplicationContext)
二、組合多個配置類
2.1、在@configuration中引入spring的xml配置檔案
2.2、在@configuration中引入其它註解配置
2.3、@configuration巢狀(巢狀的Configuration必須是靜態類)
三、@EnableXXX註解
四、@Profile邏輯組配置
五、使用外部變數
- EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage //當SpringBoot應用啟動時預設會將啟動類所在的package作為自動配置的package。
@Import(AutoConfigurationImportSelector.class) //@EnableAutoConfiguration註解是Spring Boot中配置自動裝載的總開關。
public @interface EnableAutoConfiguration {
}
boot.autoconfigure.EnableAutoConfiguration註解
-> @Import了一個AutoConfigurationImportSelector例項
-> AutoConfigurationImportSelector類(implement ImportSelector),實現了selectImports() 方法,用來篩選被@Import的Configuration類(減去exclude等)
- AutoConfigurationImportSelector.class
public class AutoConfigurationImportSelector implementsDeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
//......
@Override
publicString[] selectImports(AnnotationMetadata annotationMetadata) {
// 如果AutoConfiguration沒開,返回{}
if(!isEnabled(annotationMetadata)) {
returnNO_IMPORTS;
}
// 將spring-autoconfigure-metadata.properties的鍵值對配置載入到PropertiesAutoConfigurationMetadata物件中並返回
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 基於各種配置計算需要import的configuration和exclusion
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
// 判斷AudoConfiguration是否開啟
protectedbooleanisEnabled(AnnotationMetadata metadata) {
if(getClass() == AutoConfigurationImportSelector.class) {
// 如果配置檔案中有"spring.boot.enableautoconfiguration",返回該欄位的值;否則返回true
returngetEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
}
returntrue;
}
protectedAutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if(!isEnabled(annotationMetadata)) {
returnEMPTY_ENTRY;
}
// 獲取註解的屬性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 從META-INF/spring.factories檔案中獲取EnableAutoConfiguration所對應的configurations,但並不例項化,還要篩選
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重,List轉Set再轉List
configurations = removeDuplicates(configurations);
// 從註解的exclude/excludeName屬性中獲取排除項
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 對於不屬於AutoConfiguration的exclude報錯
checkExcludedClasses(configurations, exclusions);
// 從configurations去除exclusions
configurations.removeAll(exclusions);
// 所有AutoConfigurationImportFilter類例項化,並再進行一次篩選
configurations = filter(configurations, autoConfigurationMetadata);
// 例項化剩下configuration中的類,並把AutoConfigurationImportEvent繫結在所有AutoConfigurationImportListener子類例項上,當fireAutoConfigurationImportEvents事件被觸發時,打印出已經註冊到spring上下文中的@Configuration註解的類,打印出被阻止註冊到spring
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回(configurations, exclusions)組
return newAutoConfigurationEntry(configurations, exclusions);
}
// ......
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass()返回的是EnableAutoConfiguration.class;
// getBeanClassLoader()這裡使用的是AppClassLoader。
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
- getCandidateConfigurations方法中,SpringFactoriesLoader.loadFactoryNames(),掃描所有jar包類路徑下
META-INF/spring.factories
,並對相應的key值進行篩選,這裡使用的key值為org.springframework.boot.autoconfigure.EnableAutoConfiguration。 - 把掃描到的這些檔案的內容包裝成properties物件,以 Properties 型別(即 key-value 形式)配置,就可以將相應的實現類注入 Spirng 容器中(key為factory型別)。從properties中獲取到EnableAutoConfiguration.class(類名)對應的值,然後把它們新增在容器中
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// loadSpringFactories方法是獲取所有的springFactories
// getOrDefault是通過key,獲取到對應的類的集合。因為value是通過逗號相隔的,可以有多個,所以是list
// getOrDefault如果存在就返回,如果不存在那麼就返回給的預設值
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 三目表示式,判斷引數classLoader是否為空,如果不為空,那麼直接使用傳入的classLoader獲取META-INF/spring.factories
// 如果為空,那麼就使用系統的classLoader來獲取META-INF/spring.factories
// 總之健壯性比較強,
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
// 通過迴圈遍歷所有的META-INF/spring.factories
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 找到的每個 META-INF/spring.factories 檔案都是一個 Properties 檔案,將其內容載入到一個 Properties 物件然後處理其中的每個屬性
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 獲取工廠類名稱(介面或者抽象類的全限定名)
String factoryTypeName = ((String) entry.getKey()).trim();
// 將逗號分割的屬性值逐個取出,然後放到 結果result 中去
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 篩選出的結果集Map放入記憶體中,
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
- 在getConfigurationClassFilter與fireAutoConfigurationImportEvents方法中將其通過SpringFactoriesLoader 中的loadFactories反射對所有的配置進行篩選,例項化,並繫結到AutoConfigurationImportListener子類例項上
https://blog.csdn.net/qq_42154259/article/details/107600734
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
/**
* 通過classLoader從各個jar包的classpath下面的META-INF/spring.factories載入並解析其key-value值,然後建立其給定型別的工廠實現
*
* 返回的工廠通過AnnotationAwareOrderComparator進行排序過的。
* AnnotationAwareOrderComparator就是通過@Order註解上面的值進行排序的,值越高,則排的越靠後
*
* 如果需要自定義例項化策略,請使用loadFactoryNames方法獲取所有註冊工廠名稱。
*
* @param factoryType 介面或者抽象類的Class物件
* @param classLoader 用於載入的類載入器(可以是null,如果是null,則使用預設值)
* @throws IllegalArgumentException 如果無法載入任何工廠實現類,或者在例項化任何工廠時發生錯誤,則會丟擲IllegalArgumentException異常
*/
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
// 首先斷言,傳入的介面或者抽象類的Class物件不能為空
Assert.notNull(factoryType, "'factoryType' must not be null");
// 將傳入的classLoader賦值給classLoaderToUse
// 判斷classLoaderToUse是否為空,如果為空,則使用預設的SpringFactoriesLoader的classLoader
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 載入所有的META-INF/spring.factories並解析,獲取其配置的factoryNames
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
// 通過反射對所有的配置進行例項化。
for (String factoryImplementationName : factoryImplementationNames) {
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
// 例項化工廠,根據
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
// 根據全限定類名通過反射獲取Class物件
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
// 判斷獲取的Class物件是否從factoryType裡面來,
// 說具體點就是判斷我們配置的spring.factories中的許可權定類名所對應的類是否是對應的子類或者實現類
// 比如
// org.springframework.context.ApplicationContextInitializer=\
// org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
// org.springframework.boot.context.ContextIdApplicationContextInitializer,
// 那麼他就會驗證ConfigurationWarningsApplicationContextInitializer和ContextIdApplicationContextInitializer是否是ApplicationContextInitializer的子類
// isAssignableFrom()方法與instanceof關鍵字的區別總結為以下兩個點:
// isAssignableFrom()方法是從類繼承的角度去判斷,instanceof關鍵字是從例項繼承的角度去判斷。
// isAssignableFrom()方法是判斷是否為某個類的父類,instanceof關鍵字是判斷是否某個類的子類。
// 如果不是,則會儲存
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
// 通過反射的有參建構函式進行例項化:如果直接newInstance的話,那麼只能通過空參建構函式進行例項化。
// 通過這種方式可以通過不同引數的建構函式進行建立例項,但是這裡並沒有傳入引數,所以呼叫的是預設空慘建構函式
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
-
可見selectImports()是AutoConfigurationImportSelector的核心函式,其核心功能就是獲取spring.factories中EnableAutoConfiguration所對應的Configuration類列表,由@EnableAutoConfiguration註解中的exclude/excludeName引數篩選一遍,再由AutoConfigurationImportFilter類所有例項篩選一遍,得到最終的用於Import的configuration和exclusion。
-
該函式是被誰呼叫的呢?在org.springframework.context.annotation.ConfigurationClassParser類中被processImports()呼叫,而processImports()函式被doProcessConfigurationClass()呼叫。下面從doProcessConfigurationClass() 看起。
-
上面為@Import()的Spring的註釋的底層實現
-
接著看我們EnableAutoConfiguration.class(類名)對應的值:
-
每一個這樣的
xxxAutoConfiguration
類都是容器中的一個元件,都加入到容器中;用他們來做自動配置; -
每一個自動配置類進行自動配置功能;
-
eg :HttpEncodingAutoConfiguration
package org.springframework.boot.autoconfigure.web.servlet;
......
//表示這是一個配置類,以前編寫的配置檔案一樣,也可以給容器中新增元件
@Configuration(
proxyBeanMethods = false
)
/**
* 啟動指定類的ConfigurationProperties功能;
* 將配置檔案中對應的值和HttpProperties繫結起來;
* 並把HttpProperties加入到ioc容器中
*/
@EnableConfigurationProperties({HttpProperties.class})
/**
* Spring底層@Conditional註解
* 根據不同的條件,如果滿足指定的條件,整個配置類裡面的配置就會生效;
* 判斷當前應用是否是web應用,如果是,當前配置類生效
*/
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//判斷當前專案有沒有這個類
@ConditionalOnClass({CharacterEncodingFilter.class})
/**
* 判斷配置檔案中是否存在某個配置 spring.http.encoding.enabled;如果不存在,判斷也是成立的
* 即使我們配置檔案中不配置pring.http.encoding.enabled=true,也是預設生效的;
*/
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
//它已經和SpringBoot的配置檔案映射了
private final Encoding properties;
//只有一個有參構造器的情況下,引數的值就會從容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean //給容器中新增一個元件,這個元件的某些值需要從properties中獲取
@ConditionalOnMissingBean //判斷容器有沒有這個元件?(容器中沒有才會新增這個元件)
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
......
- 根據當前不同的條件判斷,決定這個配置類是否生效
- 一但這個配置類生效;這個配置類就會給容器中新增各種元件;這些元件的屬性是從對應的properties類中獲取的,這些類裡面的每一個屬性又是和配置檔案繫結的;
- 相應的配置類在@EnableConfigurationProperties中指定,其會在讀取我們寫的properties並將值注入類中,由
xxxAutoConfiguration
讀取 例項化的xxxxProperties類,根據屬性生成相應的bean給ApplicationContext呼叫。
所有在配置檔案中能配置的屬性都是在xxxxProperties
類中封裝著;配置檔案能配置什麼就可以參照某個功能對應的這個屬性類
@ConfigurationProperties(
prefix = "spring.http"
)
public class HttpProperties {
private boolean logRequestDetails;
private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();
我們配置時的流程:
SpringBoot啟動會載入大量的自動配置類
我們看我們需要的功能有沒有SpringBoot預設寫好的自動配置類
再來看這個自動配置類中到底配置了哪些元件;(只要我們要用的元件有,我們就不需要再來配置了)
給容器中自動配置類新增元件的時候,會從properties類中獲取某些屬性。我們就可以在配置檔案中指定這些屬性的值
xxxxAutoConfigurartion
:自動配置類;
xxxxProperties
:封裝配置檔案中相關屬性;
Apllication總體流程
SpringApplication的run方法的實現是我們本次旅程的主要線路,該方法的主要流程大體可以歸納如下:
1) 如果我們使用的是SpringApplication的靜態run方法,那麼,這個方法裡面首先要建立一個SpringApplication物件例項,然後呼叫這個建立好的SpringApplication的例項方法。在SpringApplication例項初始化的時候,它會提前做幾件事情:
- 根據classpath裡面是否存在某個特徵類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該建立一個為Web應用使用的ApplicationContext型別。
- 使用SpringFactoriesLoader在應用的classpath中查詢並載入所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在應用的classpath中查詢並載入所有可用的ApplicationListener。
- 推斷並設定main方法的定義類。
2) SpringApplication例項初始化完成並且完成設定後,就開始執行run方法的邏輯了,方法執行伊始,首先遍歷執行所有通過SpringFactoriesLoader可以查詢到並載入的SpringApplicationRunListener。呼叫它們的started()方法,告訴這些SpringApplicationRunListener,“嘿,SpringBoot應用要開始執行咯!”。
3) 建立並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。
4) 遍歷呼叫所有SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:“當前SpringBoot應用使用的Environment準備好了咯!”。
5) 如果SpringApplication的showBanner屬性被設定為true,則列印banner。
6) 根據使用者是否明確設定了applicationContextClass型別以及初始化階段的推斷結果,決定該為當前SpringBoot應用建立什麼型別的ApplicationContext並建立完成,然後根據條件決定是否新增ShutdownHook,決定是否使用自定義的BeanNameGenerator,決定是否使用自定義的ResourceLoader,當然,最重要的,將之前準備好的Environment設定給建立好的ApplicationContext使用。
7) ApplicationContext建立好之後,SpringApplication會再次藉助Spring-FactoriesLoader,查詢並載入classpath中所有可用的ApplicationContext-Initializer,然後遍歷呼叫這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經建立好的ApplicationContext進行進一步的處理。
8) 遍歷呼叫所有SpringApplicationRunListener的contextPrepared()方法。
9) 最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置載入到已經準備完畢的ApplicationContext。
10) 遍歷呼叫所有SpringApplicationRunListener的contextLoaded()方法。
11) 呼叫ApplicationContext的refresh()方法,完成IoC容器可用的最後一道工序。
12) 查詢當前ApplicationContext中是否註冊有CommandLineRunner,如果有,則遍歷執行它們。
13) 正常情況下,遍歷執行SpringApplicationRunListener的finished()方法、(如果整個過程出現異常,則依然呼叫所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常資訊一併傳入處理)****