上一篇已經瞭解了spring的相關概念,而且建立了一個Spring項目。spring中有最重要的兩個概念:IOC和AOP,咱們先從IOC入手。spring
IOC全稱Inversion of Control,中文一般翻譯爲「控制反轉」,這其實不是一種技術,而是一種思想。編程
簡單理解就是把原先咱們代碼裏面須要實現的對象建立、依賴的代碼,反轉給容器來幫忙實現。安全
IoC是什麼app
Ioc—Inversion of Control,即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味着將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,爲什麼是反轉(有反轉就應該有正轉了),哪些方面反轉了」,那咱們來深刻分析一下:框架
●誰控制誰,控制什麼:傳統Java SE程序設計,咱們直接在對象內部經過new進行建立對象,是程序主動去建立依賴對象;而IoC是有專門一個容器來建立這些對象,即由Ioc容器來控制對 象的建立;誰控制誰?固然是IoC 容器控制了對象;控制什麼?那就是主要控制了外部資源獲取(不僅是對象包括好比文件等)。ide
●爲什麼是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程序是由咱們本身在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴對象;爲什麼是反轉?由於由容器幫咱們查找及注入依賴對象,對象只是被動的接受依賴對象,因此是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。源碼分析
用圖例說明一下,傳統程序設計如圖1-1,都是主動去建立相關對象而後再組合起來:學習
圖1-1 傳統應用程序示意圖測試
當有了IoC/DI的容器後,在客戶端類中再也不主動去建立這些對象了,如圖2-2所示:ui
圖1-2有IoC/DI容器後程序結構示意圖
IoC能作什麼
IoC 不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導咱們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由咱們在類內部主動建立依賴對象,從而致使類與類之間高耦合,難於測試;有了IoC容器後,把建立和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,因此對象與對象之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得很是靈活。
其實IoC對編程帶來的最大改變不是從代碼上,而是從思想上,發生了「主從換位」的變化。應用程序本來是老大,要獲取什麼資源都是主動出擊,可是在IoC/DI思想中,應用程序就變成被動的了,被動的等待IoC容器來建立並注入它所須要的資源了。
IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:「別找咱們,咱們找你」;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找。
IoC和DI
DI—Dependency Injection,即「依賴注入」:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並不是爲軟件系統帶來更多功能,而是爲了提高組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。經過依賴注入機制,咱們只須要經過簡單的配置,而無需任何代碼就可指定目標須要的資源,完成自身的業務邏輯,而不須要關心具體的資源來自何處,由誰實現。
理解DI的關鍵是:「誰依賴誰,爲何須要依賴,誰注入誰,注入了什麼」,那咱們來深刻分析一下:
●誰依賴於誰:固然是應用程序依賴於IoC容器;
●爲何須要依賴:**應用程序須要IoC容器來提供對象須要的外部資源**;
●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;
●注入了什麼:就是注入某個對象所須要的外部資源(包括對象、資源、常量數據)。
IoC和DI由什麼關係呢?其實它們是同一個概念的不一樣角度描述,因爲控制反轉概念比較含糊(可能只是理解爲容器控制對象這一個層面,很難讓人想到誰來維護對象關係),因此2004年大師級人物Martin Fowler又給出了一個新的名字:「依賴注入」,相對IoC 而言,「**依賴注入」明確描述了「被注入對象依賴IoC容器配置依賴對象」。
相信經過上面的文章,對IOC的理解會更深。下面講講三種依賴注入的方式
構造方法注入
顧名思義,構造方法注入,就是被注入對象能夠經過在其構造方法中聲明依賴對象的參數列表, 讓外部(一般是IoC容器)知道它須要哪些依賴對象
public classA(IinterfaceA a,IinterfaceB b){ this.a=a; this.b=b; }
構造方法注入方式比較直觀,對象被構造完成後,即進入就緒狀態,能夠立刻使用。
setter 方法注入
對於JavaBean對象來講,一般會經過setXXX()和getXXX()方法來訪問對應屬性。這些setXXX()方法統稱爲setter方法,getXXX()固然就稱爲getter方法。
public class classB(){ private IinterfaceA a; private IinterfaceB b; public IinterfaceA getIinterfaceA(){ return a; } public void setIinterfaceA(IinterfaceA a){ this.a=a; } public IinterfaceB getIinterfaceB(){ return b; } public void setIinterfaceB(IinterfaceB b){ this.b=b; } }
接口注入
相對於前兩種注入方式來講,接口注入沒有那麼簡單明瞭。被注入對象若是想要IoC Service Provider爲其注入依賴對象,就必須實現某個接口。這個接口提供一個方法,用來爲其注入依賴對象。IoC Service Provider最終經過這些接口來了解應該爲被注入對象注入什麼依賴對象。
建立Person (被注入對象)要實現的接口
interface UserInject{ void injectUser(User user);//這裏必須 是被注入對象依賴的對象 }
Person 對象實現接口
class Person implements UserInject{ private User user; public Person(){} @Override public void injectUser(User user) { this.user = user;//實現注入方法,外部經過此方法給此對象注入User對象 } }
外部調injectUser方法爲Persion對象注入User對象,此即接口注入
三種注入方式的比較
接口注入。從注入方式的使用上來講,接口注入是如今不甚提倡的一種方式,基本處於「退役狀態」。由於它強制被注入對象實現沒必要要的接口,帶有侵入性。而構造方法注入和setter方法注入則不須要如此。
構造方法注入。這種注入方式的優勢就是,對象在構造完成以後,即已進入就緒狀態,能夠 9立刻使用。缺點就是,當依賴對象比較多的時候,構造方法的參數列表會比較長。而經過反射構造對象的時候,對相同類型的參數的處理會比較困難,維護和使用上也比較麻煩。並且在Java中,構造方法沒法被繼承,沒法設置默認值。對於非必須的依賴處理,可能須要引入多個構造方法,而參數數量的變更可能形成維護上的不便。
setter方法注入。由於方法能夠命名,因此setter方法注入在描述性上要比構造方法注入好一些。 另外,setter方法能夠被繼承,容許設置默認值,並且有良好的IDE支持。缺點固然就是對象沒法在構造完成後立刻進入就緒狀態。
綜上所述,構造方法注入和setter方法注入由於其侵入性較弱,且易於理解和使用,因此是如今使用最多的注入方式;而接口注入由於侵入性較強,近年來已經不流行了。
在學習spring的具體配置以前,先了解下源碼的基本結構。上一篇的測試代碼
ApplicationContext ctx=new ClassPathXmlApplicationContext("META-INF/applicationContext.xml"); //獲取bean的實例 HelloWorld t=(HelloWorld) ctx.getBean("hello");
咱們大體分析下過程:
經過Resource對象加載配置文件
解析配置文件,獲得bean
解析bean,id做爲bean的名字,class用於反射獲得bean的實例(Class.forName(className));
調用getBean的時候,從容器中返回對象實例。
固然這只是簡單的理解,IOC核心內容是beanFactory與ApplicationContext
BeanFactory
BeanFactory 是 Spring 的「心臟」。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 來實例化、配置和管理 Bean,BeanFactory有着龐大的繼承、實現體系,有衆多的子接口、實現類。
BeanFactory做爲一個主接口不繼承任何接口,暫且稱爲一級接口。
有3個子接口繼承了它,進行功能上的加強。這3個子接口稱爲二級接口。
ConfigurableBeanFactory能夠被稱爲三級接口,對二級接口HierarchicalBeanFactory進行了再次加強,它還繼承了另外一個外來的接口SingletonBeanRegistry
ConfigurableListableBeanFactory是一個更強大的接口,繼承了上述的全部接口,無所不包,稱爲四級接口。(這4級接口是BeanFactory的基本接口體系。繼續,下面是繼承關係的2個抽象類和2個實現類:)
AbstractBeanFactory做爲一個抽象類,實現了三級接口ConfigurableBeanFactory大部分功能。
AbstractAutowireCapableBeanFactory一樣是抽象類,繼承自AbstractBeanFactory,並額外實現了二級接口AutowireCapableBeanFactory
DefaultListableBeanFactory繼承自AbstractAutowireCapableBeanFactory,實現了最強大的四級接口ConfigurableListableBeanFactory,並實現了一個外來接口BeanDefinitionRegistry,它並不是抽象類。
最後是最強大的XmlBeanFactory,繼承自DefaultListableBeanFactory,重寫了一些功能,使本身更強大。
最基本的IOC容器接口BeanFactory
public interface BeanFactory { /** * 用來引用一個實例,或把它和工廠產生的Bean區分開,就是說,若是一個FactoryBean的名字爲a,那麼,&a會獲得那個Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四個不一樣形式的getBean方法,獲取實例 */ //根據bean的名字,獲取在IOC容器中獲得bean實例 Objecpublic interface BeanFactory { /** * 用來引用一個實例,或把它和工廠產生的Bean區分開,就是說,若是一個FactoryBean的名字爲a,那麼,&a會獲得那個Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四個不一樣形式的getBean方法,獲取實例 */ //根據bean的名字,獲取在IOC容器中獲得bean實例 Object getBean(String name) throws BeansException; //根據bean的名字和Class類型來獲得bean實例,增長了類型安全驗證機制。 <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; // 是否存在 boolean containsBean(String name); // 是否爲單實例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否爲原型(多實例) boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 名稱、類型是否匹配 boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException; //獲得bean實例的Class類型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);// 根據實例的名字獲取實例的別名 getBean(String name) throws BeansException; //根據bean的名字和Class類型來獲得bean實例,增長了類型安全驗證機制。 <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; // 是否存在 boolean containsBean(String name); // 是否爲單實例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否爲原型(多實例) boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 名稱、類型是否匹配 boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException; //獲得bean實例的Class類型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);// 根據實例的名字獲取實例的別名
BeanFactory接口只是作了最基本的定義,裏面無論如何定義和加載,只關心如何獲得對象,要知道如何獲得對象,必須看具體的實現類,其中XmlBeanFactory就是針對最基本的ioc容器的實現。
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader; public XmlBeanFactory(Resource resource) throws BeansException { this(resource, (BeanFactory)null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader = new XmlBeanDefinitionReader(this); this.reader.loadBeanDefinitions(resource); } }
使用:
//根據Xml配置文件建立Resource資源對象,該對象中包含了BeanDefinition的信息 Resource resource = new ClassPathResource("META-INF/applicationContext.xml"); //建立XmlBeanDefinitionReader讀取器,用於載入BeanDefinition。之因此須要BeanFactory做爲參數,是由於會將讀取的信息回調配置給factory BeanFactory beanFactory = new XmlBeanFactory(resource); HelloWorld helloWorld = beanFactory.getBean("hello",HelloWorld.class); System.out.println(helloWorld.getInfo());
ApplicationContext
ApplicationContext是Spring提供的一個高級的IoC容器,它除了可以提供IoC容器的基本功能外,還爲用戶提供瞭如下的附加服務。
支持信息源,能夠實現國際化。(實現MessageSource接口)
訪問資源。(實現ResourcePatternResolver接口)
支持應用事件。(實現ApplicationEventPublisher接口)
二者的區別
1.BeanFactroy採用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調用getBean()),纔對該Bean進行加載實例化,這樣,咱們就不能發現一些存在的Spring的配置問題。而ApplicationContext則相反,它是在容器啓動時,一次性建立了全部的Bean。這樣,在容器啓動時,咱們就能夠發現Spring中存在的配置錯誤。 相對於基本的BeanFactory,ApplicationContext 惟一的不足是佔用內存空間。當應用程序配置Bean較多時,程序啓動較慢。
BeanFacotry延遲加載,若是Bean的某一個屬性沒有注入,BeanFacotry加載後,直至第一次使用調用getBean方法纔會拋出異常;而ApplicationContext則在初始化自身是檢驗,這樣有利於檢查所依賴屬性是否注入;因此一般狀況下咱們選擇使用 ApplicationContext。 應用上下文則會在上下文啓動後預載入全部的單實例Bean。經過預載入單實例bean ,確保當你須要的時候,你就不用等待,由於它們已經建立好了。
2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但二者之間的區別是:BeanFactory須要手動註冊,而ApplicationContext則是自動註冊。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。並且 beanFactory 的許多功能須要經過編程實現而 Applicationcontext 能夠經過配置實現。好比後處理 bean , Applicationcontext 直接配置在配置文件便可而 beanFactory 這要在代碼中顯示的寫出來才能夠被容器識別。 )
3.beanFactory主要是面對與 spring 框架的基礎設施,面對 spring 本身。而 Applicationcontex 主要面對與 spring 使用的開發者。基本都會使用 Applicationcontex 並不是 beanFactory 。