Spring有兩個核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們都可代表Spring容器,Spring容器是生成Bean實例的工廠,並且管理容器中的Bean。
Bean是Spring管理的基本單位,在基於Spring的Java EE應用中,所有的組件都被當成Bean處理,包括數據源、Hibernate的SessionFactory、事務管理器等。在Spring中,Bean的是一個非常廣義的概念,任何的Java對象、Java組件都被當成Bean處理。
而且應用中的所有組件,都處於Spring的管理下,都被Spring以Bean的方式管理,Spring負責創建Bean實例,並管理他們的生命周期。Bean在Spring容器中運行,無須感受Spring容器的存在,一樣可以接受Spring的依賴注入,包括Bean屬性的注入,協作者的注入、依賴關系的注入等。
Spring容器負責創建Bean實例,所以需要知道每個Bean的實現類,Java程序面向接口編程,無須關心Bean實例的實現類;但是Spring容器必須能夠精確知道每個Bean實例的實現類,因此Spring配置文件必須精確配置Bean實例的實現類。
一、Spring容器
Spring容器最基本的接口就是BeanFactor。BeanFactory負責配置、創建、管理Bean,他有一個子接口:ApplicationContext,因此也稱之為Spring上下文。Spring容器負責管理Bean與Bean之間的依賴關系。
BeanFactory接口包含以下幾個基本方法:
Ø Boolean containBean(String name):判斷Spring容器是否包含id為name的Bean實例。
Ø <T> getBean(Class<T> requiredTypr):獲取Spring容器中屬於requiredType類型的唯一的Bean實例。
Ø Object getBean(String name):返回Sprin容器中id為name的Bean實例。
Ø <T> T getBean(String name,Class requiredType):返回容器中id為name,並且類型為requiredType的Bean
Ø Class <?> getType(String name):返回容器中指定Bean實例的類型。
調用者只需使用getBean()方法即可獲得指定Bean的引用,無須關心Bean的實例化過程。即Bean實例的創建過程完全透明。
在使用BeanFactory接口時,我們一般都是使用這個實現類:org.springframework.beans.factory.xml.XmlBeanFactory。然而ApplicationContext作為BeanFactory的子接口,使用它作為Spring容器會更加方便。它的實現類有:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext、AnnotationConfigApplicationContext。
創建Spring容器實例時,必須提供Spring容器管理的Bean的詳細配置信息。Spring的配置信息通常采用xml配置文件來設置,因此,創建BeanFactory實例時,應該提供XML配置文件作為參數。
XML配置文件通常使用Resource對象傳入。Resource接口是Spring提供的資源訪問接口,通過使用該接口,Spring能夠以簡單、透明的方式訪問磁盤、類路徑以及網絡上的資源。
對於Java EE應用而言,可在啟動Web應用時自動加載ApplicationContext實例,接受Spring管理的Bean無須知道ApplicationContext的存在。一般使用如下方式實例化BeanFactory:
1 //搜索當前文件路徑下的bean.xml文件創建Resource對象 2 InputStreamSource isr = new FileSystemResource("bean.xml"); 3 //以Resource對象作為參數創建BeanFactory實例 4 XmlBeanFactory factory = new XmlBeanFactory((Resource) isr); 5 或 6 ClassPathResource res = new ClassPathResource("bean.xml"); 7 //以Resource對象作為參數創建BeanFactory實例 8 XmlBeanFactory factory = new XmlBeanFactory(res);
但是如果應用里面有多個屬性配置文件,則應該采用BeanFactory的子接口ApplicationContext來創建BeanFactory的實例。ApplicationContext通常使用如下兩個實現類:
FileSystemXmlApplicationContext:以基於文件系統的XML配置文件創建ApplicationContext實例。
ClassPathXmlApplicationContext:以類加載路徑下的XML配置文件創建的ApplicationContext實例。
如果需要同時加載多個XML配置文件,采用如下方式:
1 //搜索CLASSPATH路徑,以classpath路徑下的bean.xml、service.xml文件創建applicationContext 2 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"bean.xml","service.xml"}); 3 4 //以指定路徑下的bean.xml、service.xml文件創建applicationContext 5 ApplicationContext ctx1 = new FileSystemXmlApplicationContext(new String[]{"bean.xml","service.xml"});
二、讓Bean獲取Spring容器
在前面簡單的介紹了Spring容器。在Spring中我們可以使用Spring容器中getBean()方法來獲取Spring容器中的Bean實例。在這樣的訪問模式下,程序中總是持有Spring容器的引用。但是在實際的應用中,Spring容器通常是采用聲明式方式配置產生:記開發者只要在web.xml文件中配置一個Listener,該Listener將會負責初始化Spring容器。在這種情況下,容器中Bean處於容器管理下,無須主動訪問容器,只需要接受容器的注入管理即可。同時Bean實例的依賴關系通常也是由容器冬天注入,無須Bean實例主動請求。
在這種情況下,Sprig容器中Bean通常不會需要訪問容器中其他的Bean—采用依賴注入,讓Spring把被依賴的Bean注入到依賴的Bean中即可。
實現BeanFactoryAware接口的Bean,擁有訪問的BeanFactory容器的能力,實現BeanFactoryAware接口的Bean實例將會擁有對容器的訪問能力。BeanFactoryAware接口僅有如下一個方法:
SetBeanFactory(BeanFactory beanFactory):該方法有一個參數beanFactory,該參數指向創建它的BeanFactory。
該方法將由Spring調動,當Spring調用該方法時會將Spring容器作為參數傳入該方法。
1 public class Chinese implements ApplicationContextAware{ 2 3 //將BeanFactory容器以成員變量保存 4 private ApplicationContext ctx; 5 /** 6 * 實現ApplicationContextAware接口實現的方法 7 */ 8 public void setApplicationContext(ApplicationContext cyx) 9 throws BeansException { 10 this.ctx = ctx; 11 } 12 13 //獲取ApplicationContext的測試方法 14 public ApplicationContext getContext(){ 15 return ctx; 16 } 17 18 }
上面的Chinese類實現了ApplicationContext接口,並實現了該接口提供的setApplicationContextAware()方法,這就使得該Bean實例可以直接訪問到創建她的Spring容器。
將該Bean部署在Spring容器中。
測試類:
該程序先通過實例化的方法來獲取ApplicationContext,然后通過chinese Bean來獲得BeanFactory,並將兩者進行比較。
1 public class ChineseTest { 2 3 public static void main(String[] args) { 4 ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); 5 Chinese c = ctx.getBean("chinese",Chinese.class); 6 System.out.println(c.getContext()); 7 8 System.out.println(c.getContext()==ctx); 9 10 } 11 }
結果如下:
true
上面的代碼雖然實現了ApplicationContextAware接口讓Bean擁有了訪問容器的能力,但是污染了代碼,導致代碼與Spring接口耦合在一起。所以,如果不是特別需要,一般不建議直接訪問容器。