Spring Boot註解
自動配置
Spring使用@Configuration作為配置的註解,當Spring發現某個類使用了@Configuration標註了,就去將該類下尋找使用@Bean註解的方法建立bean並放入到容器中 @Configuration
spring boot的自動配置是通過@ConditionalOnXxx(@ConditionalOnClass、@ConditionalOnMissingBean等) 來實現的,該型別的註解都是基於@Conditional註解來實現的
關於自動配置可以通過瀏覽spring-boot-autoconfigure.jar下的spring.factories配置檔案,檢視所有的自動配置
理解Spring Boot的自動配置先要理解@Conditional註解 @Bean: 將當前物件放入到Spring的IOC容器中來管理 @Conditional:滿足特定條件時才會建立一個Bean放入到IOC容器,SpringBoot就是利用這個特性進行自動配置的
寫一個小示例來理解下@Conditional的作用: 在不同的作業系統中列舉某個目錄下的檔案的命令是不同的,類unix系統使用ls命令,windows命令使用dir命令。現在定義兩個類分別對應著兩種系統,只將當前系統對應的類放入到IOC容器中,另一個類不放進IOC中 程式碼: OnWindowsCondition
public class OnWindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
}
}
OnUnixCondition
public class OnUnixCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String osName = conditionContext.getEnvironment().getProperty("os.name");
return osName.contains("unix") || osName.contains("linux") || osName.contains("Mac OS X");
}
}
ListService
public interface ListService {
public String cmd();
}
public class WindowsListService implements ListService {
@Override
public String cmd() {
return "dir";
}
}
public class UnixListService implements ListService {
@Override
public String cmd() {
return "ls";
}
}
ListSesrviceConfig 使用@Bean來標註將物件放入到IOC容器中,但是如果有@Conditional只有在滿足條件註解才會將物件放入到IOC容器中,不滿足就不會放入容器
@Configuration
public class ListSesrviceConfig {
@Bean
@Conditional(OnWindowsCondition.class)
public ListService windowsListService(){
return new WindowsListService();
}
@Bean
@Conditional(OnUnixCondition.class)
public ListService unixListService(){
return new UnixListService();
}
}
main() 會根據context.getEnvironment().getProperty(“os.name”)獲取當前的系統環境.在Windows電腦上執行就會裝載 WindowsListService,在Linux等環境上執行就會裝載UnixListService
@ConditionalOnXxx都是組合@Conditional元註解,使用了不同的條件Condition
- @ConditionalOnBean:當容器裡有指定Bean的條件下
- @ConditionalOnClass:當類路徑下有指定類的條件下
- @ConditionalOnExpression:基於SpEL表示式作為判斷條件
- @ConditionalOnJava:基於JV版本作為判斷條件
- @ConditionalOnJndi:在JNDI存在的條件下差在指定的位置
- @ConditionalOnMissingBean:當容器裡沒有指定Bean的情況下
- @ConditionalOnMissingClass:當類路徑下沒有指定類的條件下
- @ConditionalOnNotWebApplication:當前專案不是Web專案的條件下
- @ConditionalOnProperty:指定的屬性是否有指定的值
- @ConditionalOnResource:類路徑是否有指定的資源
- @ConditionalOnSingleCandidate:當指定Bean在容器中只有一個,或者雖然有多個但是指定首選Bean
- @ConditionalOnWebApplication:當前專案是Web專案的條件下
@SpringBootApplication
啟動入口:Application
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
@SpringBootApplication 是一個複合註解,由以下3個註解組成:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
其中@SpringBootConfiguration實際上是@Configuration
@SpringBootConfiguration
將@SpringBootApplication註解替換成其等價的三個註解是完全的相等的,可以啟動並正常訪問
@ComponentScan
元件掃描這注解,即使沒有使用過註解也經常在spring的配置檔案中使用過 <context:component-scan base-package=“com.xxx.xxx”/>, 元件掃描就是掃描指定的包下的類,並載入符合條件的元件(比如@Component、@Service、@Repository等)或者bean定義,最終將這些bean定義載入到IoC容器中,如果不指定basePackage則預設Spring框架實現會從宣告@ComponentScan所在類的package進行掃描。Spring Boot 中的@ComponentScan沒有指定basePackage,所以會從.下開始掃描,所以,一般Application類要放在groupId.artifactId包下
@Configuration
@Configuration 就是指的Java Config(Java 配置),是一個Ioc容器類,相當於傳統專案中見到的一個spring 的xml配置檔案 一個空Java Config類 相當於一個空的xml配置檔案,都是用來作為配置bean的容器
在xml中配置一個bean,就等於在Java Config中定義一個方法,使用@Bean註解就相當於xml中的bean標籤,bean標籤中的id就是方法的名稱, bean標籤中的class就是Java Config中的返回值,Java Config中的方法的內部就是建立一個例項
xml中一個bean的屬性需要引用另一個bean時,在Java Config中需要先定義出需要的依賴類,然後通過Setter方法或者構造方法給依賴的屬性賦值,從而創建出一個完整的例項
@EnableAutoConfiguration
Spring中有很多以Enable開頭的註解,其作用就是藉助@Import來收集並註冊特定場景相關的bean,並載入到IoC容器。@EnableAutoConfiguration就是藉助@Import來收集所有符合自動配置條件的bean定義,並載入到IoC容器
@EnableAutoConfiguration通過@Import中的AutoConfigurationImportSelector,可以幫助SpringBoot應用將所有符合條件的@Configuration配置都載入到當前SpringBoot建立並使用的IoC容器(ApplicationContext)
SpringFactoriesLoader 主要功能就是從指定的配置檔案META-INF/spring.factories載入配置。配合@EnableAutoConfiguration使用的話,它更多是提供一種配置查詢的功能支援,即根據@EnableAutoConfiguration的完整類名org.springframework.boot.autoconfigure.EnableAutoConfiguration作為查詢的Key, 獲取對應的一組@Configuration類
spring.factories
@EnableAutoConfiguration自動配置就是從classpath中搜尋所有的META-INF/spring.factories配置檔案,並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項通過反射(Java Refletion)例項化為對應的標註了@Configuration的JavaConfig形式的IoC容器配置類,然後彙總為一個並載入到IoC容器。 SpringApplication的run方法的實現是我們本次旅程的主要線路,該方法的主要流程大體可以歸納如下:
- 如果我們使用的是SpringApplication的靜態run方法,那麼,這個方法裡面首先要建立一個SpringApplication物件例項,然後呼叫這個建立好的SpringApplication的例項方法。在SpringApplication例項初始化的時候,它會提前做幾件事情: 根據classpath裡面是否存在某個特徵類(org.springframework.web.context.ConfigurableWebApplicationContext)來決定是否應該建立一個為Web應用使用的ApplicationContext型別。 使用SpringFactoriesLoader在應用的classpath中查詢並載入所有可用的ApplicationContextInitializer。 使用SpringFactoriesLoader在應用的classpath中查詢並載入所有可用的ApplicationListener。 推斷並設定main方法的定義類。
- SpringApplication例項初始化完成並且完成設定後,就開始執行run方法的邏輯了,方法執行伊始,首先遍歷執行所有通過SpringFactoriesLoader可以查詢到並載入的SpringApplicationRunListener。呼叫它們的started()方法,告訴這些SpringApplicationRunListener,“嘿,SpringBoot應用要開始執行咯!”。
- 建立並配置當前Spring Boot應用將要使用的Environment(包括配置要使用的PropertySource以及Profile)。
- 遍歷呼叫所有SpringApplicationRunListener的environmentPrepared()的方法,告訴他們:“當前SpringBoot應用使用的Environment準備好了咯!”。
- 如果SpringApplication的showBanner屬性被設定為true,則列印banner。
- 根據使用者是否明確設定了applicationContextClass型別以及初始化階段的推斷結果,決定該為當前SpringBoot應用建立什麼型別的ApplicationContext並建立完成,然後根據條件決定是否新增ShutdownHook,決定是否使用自定義的BeanNameGenerator,決定是否使用自定義的ResourceLoader,當然,最重要的,將之前準備好的Environment設定給建立好的ApplicationContext使用。
- ApplicationContext建立好之後,SpringApplication會再次藉助Spring-FactoriesLoader,查詢並載入classpath中所有可用的ApplicationContext-Initializer,然後遍歷呼叫這些ApplicationContextInitializer的initialize(applicationContext)方法來對已經建立好的ApplicationContext進行進一步的處理。
- 遍歷呼叫所有SpringApplicationRunListener的contextPrepared()方法。
- 最核心的一步,將之前通過@EnableAutoConfiguration獲取的所有配置以及其他形式的IoC容器配置載入到已經準備完畢的ApplicationContext。
- 遍歷呼叫所有SpringApplicationRunListener的contextLoaded()方法。
- 呼叫ApplicationContext的refresh()方法,完成IoC容器可用的最後一道工序。
- 查詢當前ApplicationContext中是否註冊有CommandLineRunner,如果有,則遍歷執行它們。
- 正常情況下,遍歷執行SpringApplicationRunListener的finished()方法、(如果整個過程出現異常,則依然呼叫所有SpringApplicationRunListener的finished()方法,只不過這種情況下會將異常資訊一併傳入處理) 去除事件通知點後,整個流程如下: