一、首先看applicationContext.xml里的配置項bean
我們采用xml配置文件的方式對bean進行聲明和管理,每一個bean標簽都代表著需要被創建的對象并通過property標簽可以為該類注入其他依賴對象,通過這種方式Spring容器就可以成功知道我們需要創建那些bean實例
二、ApplicationContext, Spring的容器
然后通過ClassPathXmlApplicationContext去加載spring的配置文件,接著獲取想要的實例bean并調用相應方法執行。對于ClassPathXmlApplicationContext默認加載classpath路徑下的文件,只需指明對應文件的classpath路徑下的配置文件名字即可。如果存在多個配置文件,ClassPathXmlApplicationContext是一個可以接收可變參數的構造函數。實際上ClassPathXmlApplicationContext還有一個孿生兄弟FileSystemXmlApplicationContext,它默認為項目工作路徑 即項目的根目錄 ,至于使用哪個,個人覺得沒多大的差別。
這里, 不管用按哪個方法去獲取spring的配置文件, 返回的都是一個ApplicationContext, Spring的IOC的容器, 但實際上ApplicationContext是一個接口:
?
這里的ConfigurableApplicationContext子接口,給我們提供了一些方法close(), refresh(), 可以讓ApplicationContext刷新和關閉的方法, 后面要用到,這里先認識一下。
ApplicationContext在初始化的時候, 就實例化所有單列的Bean。
具體的從ApplicationContext容器中獲取對象實例的方法getBean:
注意: 平時,我都是用id值來獲取的, 雖然從xxx.class也可以獲取, 但是用這個有個限制: ApplicationContext只有一個這個類型的對象實例, 才能用, 否則會報錯。
三、依賴注入
Spring通過標簽實現依賴注入, Spring支持的注入方式有三種:
①屬性注入
②構造器注入
③工廠方法注入(很少使用, 也不推薦使用,這個就不講了)
1、屬性注入, 也叫Setter注入
Setter注入顧名思義,被注入的屬性需要有set方法, Setter注入支持簡單類型和引用類型,Setter注入是在bean實例創建完成后執行的。直接觀察前面的案例,對象注入使用<property>的ref屬性,對象注入同時也可以注入簡單值和map、set、list、數組,簡單值注入使用<property>的value屬性。
2、構造函數注入
構造注入也就是通過構造方法注入依賴,構造函數的參數一般情況下就是依賴項,spring容器會根據bean中指定的構造函數參數來決定調用那個構造函數,同樣看一個案例:
當然跟setter注入一樣,構造注入也可傳入簡單值類型和集合類型,這個比較簡單,不啰嗦。需要注意的是,當一個bean定義中有多個<constructor-arg>標簽時,它們的放置順序并不重要,因為Spring容器會通過傳入的依賴參數與類中的構造函數的參數進行比較,嘗試找到合適的構造函數。在某些情況下,如某個類,帶有兩個構造函數,參數類型和個數都是一樣的,只是順序不同,這在class的定義中是允許的,但對于Spring容器來說默認會只會去調用前面的。
?
?
如果我們要指定使用哪個構造方法也是可以的,在<constructor-arg>標簽中存在一個index的屬性,通過index屬性可以告訴spring容器傳遞的依賴參數的順序,下面的配置將會令Spring容器成功找到第二個構造函數并調用創建實例。
在日常的開發中,setter注入和構造注入經常會混合使用, 構造注入中index和type也可以混合使用,這并不用感覺到詫異,后面我們還會分析到注解裝配,它在開發中將更為常用。
補充知識點:循環依賴
除了上述的現象,在構造函數注入還有一個無法解決的循環依賴的問題,如下有兩個bean,A和B,這兩個bean通過構造函數互為依賴,這種情況下Spring容器將無法實例化這兩個bean。
這是由于A被創建時,希望B被注入到自身,然而,此時B還有沒有被創建,而且B也依賴于A,這樣將導致Spring容器左右為難,無法滿足兩方需求,最后腦袋奔潰,拋出異常。解決這種困境的方式是使用Setter依賴,但還是會造成一些不必要的困擾,因此,強烈不建議在配置文件中使用循環依賴。