在傳統的開發步驟中,若是一個類依賴於另外一個類的實例才能完成任務的話,都須要開發者手動new一個實例。以後手動設置到任務類中。但spring的出現,爲咱們提供了一種稱爲「依賴注入」(Dependency Inject)的機制。bean的實例化在spring container內部完成,開發者只須要從xml或java code來配置bean達到定製實例化bean。並且,可讓咱們經過註解的方式,爲咱們自動注入須要的依賴。
這樣,開發者只須要遵循面向接口來開發應用,把實例化具體類和注入依賴的步驟抽離出業務代碼,達到解耦的要求。若是哪天須要使用其餘的接口實現依賴,只須要將新的實現配置進spring container。其餘業務代碼無需更改。這篇文章將圍繞着spring是如何實現bean的實例化、如何實現依賴注入來展開,剖析其內部運行機制java
// 定義一個簡單的bean
public class Car {
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
// 定義一個配置類,向Spring bean container註冊bean
@Configuration
public class Config {
@Bean
public Car car() {
Car car = new Car();
car.setColor("red");
return car;
}
}
// 編寫一個測試類來初始化spring bean container,再從其中獲取咱們在Config類中定義的bean的實例
public class ContextTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Car car = context.getBean(Car.class);
System.out.println(car.getColor());
}
}
複製代碼
運行這個用例,spring bean container將在其內部對咱們注入的bean進行實例化、初始化兩個步驟。因爲這裏沒有定義bean的做用域,所以咱們註冊的這個bean(即Car)將以單例的形式存在於container中。每一次調用context.getBean(Car.class)返回的都是同一個Car的實例。spring
說明:如下的流程分析,只圍繞spring容器實例化singleton bean(即單例)的主流程進行數據庫
對於spring這樣一個比較複雜的框架,其中包含了各類繁雜的業務邏輯。若是一開始就直接深究到每個細節,咱們沒法對spring container有一個比較全局的觀感,所以在這裏先把整個主流程畫出來,創建一個全局的藍圖。 app
註冊和解析BeanDefinition,發生在AnnotationConfigApplicationContext#register流程中,其方法內部使用了AnnotatedBeanDefinitionReader#register來實現BeanDefinition的解析和註冊;並且在實例化AnnotatedBeanDefinitionReader後,當即向container註冊了多個BeanPostProcessor的BeanDefinition(應用於bean的實例化過程)框架
在AnnotationConfigApplicationContext內部,組合了DefaultListableBeanFactory。在prepareBeanFactory(beanFactory)方法的調用過程當中,向beanfactory注入了環境變量、環境屬性等。並且注入了多個BeanPostProcessor。源碼分析
到了這一步,此時的container已經註冊了一系列的BeanFactoryPostProcessor、BeanPostProcessor和應用層相關的bean的BeanDefinition(如當前測試用例的Car)。因爲此時全部的bean(包括BeanFactoryPostProcessor、BeanPostProcessor已經應用層的bean)都是以BeanDefinition存在於container中,並未實例化。這就提供了一個機會,添加特定的BeanFactoryPostProcessor,讓spring在實例化bean以前,能夠定製修改BeanDefinition中的一些數據(如經常使用的PropertyPlaceholderConfigurer,從外部properties文件讀取配置,定義bean的屬性);
默認狀況下,只調用了ConfigurationClassPostProcessor,做用:post
繼續上一步流程,處理完BeanDefinition以後,對前面流程中註冊到beanfactory中的BeanPostProcessor進行實例化,並添加到beanfactory中的BeanPostProcessor處理隊列中。通過這一步以後,beanfactory中的BeanPostProcessor隊列存在如下BeanPostProcessor:測試
這個步驟是經過調用DefaultListableBeanFactory#preInstantiateSingletons()方法完成的。實現了預加載全部已註冊的bean,這也是ApplicationContext與BeanFactory實現類的區別,在BeanFactory實現類中,只有對一個bean調用getBean(beanname)方法以後纔會進行bean的加載。而ApplicationContext則是直接觸發其內部的BeanFactory加載全部定義好的bean。
在加載bean的過程當中,涉及到三個步驟:ui
經過調用AbstractAutowireCapableBeanFactory#createBeanInstance來實例化bean(在這以前,spring提供了一個機會能夠經過BeanPostProcessor來建立bean,而不是常規的bean實例化,跟AOP相關)。
實例化後,調用了MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition,涉及到的實現類有:this
實例化bean以後,就須要爲bean填充bean的屬性值了。這一步主要是經過調用:InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation: 能夠直接對bean填充屬性,以後直接返回。忽略後續的全部填充行爲。默認沒有進行這一步流程,而是不作任何處理,進入下一個流程
進行到這一步時,bean已經從BeanDefinition實例化爲了bean的實例,而且填充了屬性。是時候進行一下從外部而來的初始化邏輯了(非BeanDefinition相關的)。主要是:
以上咱們梳理了spring container加載bean的主體流程,spring爲咱們提供了幾大擴展點: