控制反轉顯然是一個抽象的概念,我們舉一個鮮明的例子來說明。
在現實生活中,人們要用到一樣東西的時候,第一反應就是去找到這件東西,比如想喝新鮮橙汁,在沒有飲品店的日子裡,最直觀的做法就是:買果汁機、買橙子,然後準備開水。值得注意的是:這些都是你自己「主動」創造的過程,也就是說一杯橙汁需要你自己創造。
然而到了今時今日,由於飲品店的盛行,當我們想喝橙汁時,第一想法就轉換成了找到飲品店的聯繫方式,通過電話等渠道描述你的需要、地址、聯繫方式等,下訂單等待,過一會兒就會有人送來橙汁了。
請注意你並沒有「主動」去創造橙汁,橙汁是由飲品店創造的,而不是你,然而也完全達到了你的要求,甚至比你創造的要好上那麼一些。
這就是一種控制反轉的理念,上述的例子已經很好的說明瞭問題,我們再來描述一下控制反轉的概念:控制反轉是一種通過描述(在 Java 中可以是 XML 或者註解)並通過第三方(Spring)去產生或獲取特定對象的方式。
主動創建的模式中,責任歸於開發者,而在被動的模式下,責任歸於 IoC 容器,基於這樣的被動形式,我們就說對象被控制反轉了。(也可以說是反轉了控制)
Spring 會提供 IoC 容器來管理和容納我們所開發的各種各樣的 Bean,並且我們可以從中獲取各種發布在 Spring IoC 容器裏的 Bean,並且通過描述可以得到它。
Spring IoC 容器的設計主要是基於以下兩個介面:
其中 ApplicationContext 是 BeanFactory 的子介面之一,換句話說:BeanFactory 是 Spring IoC 容器所定義的最底層介面,而 ApplicationContext 是其最高級介面之一,並對 BeanFactory 功能做了許多的擴展,所以在絕大部分的工作場景下,都會使用 ApplicationContext 作為 Spring IoC 容器。
從上圖中我們可以幾乎看到, BeanFactory 位於設計的最底層,它提供了 Spring IoC 最底層的設計,為此,我們先來看看該類中提供了哪些方法:
由於這個介面的重要性,所以有必要在這裡作一下簡短的說明:
① 按照類型拿 bean:
bean = (Bean) factory.getBean(Bean.class);
注意:要求在 Spring 中只配置了一個這種類型的實例,否則報錯。(如果有多個那 Spring 就懵了,不知道該獲取哪一個)
② 按照 bean 的名字拿 bean:
bean = (Bean) factory.getBean("beanName");
注意:這種方法不太安全,IDE 不會檢查其安全性(關聯性)
③ 按照名字和類型拿 bean:(推薦)
bean = (Bean) factory.getBean("beanName", Bean.class);
注意:在默認情況下,【isSingleton】為 ture,而【isPrototype】為 false
這就是 Spring IoC 最底層的設計,所有關於 Spring IoC 的容器將會遵守它所定義的方法。
根據 ApplicationContext 的類繼承關係圖,可以看到 ApplicationContext 介面擴展了許許多多的介面,因此它的功能十分強大,所以在實際應用中常常會使用到的是
ApplicationContext 介面,因為 BeanFactory 的方法和功能較少,而 ApplicationContext 的方法和功能較多。
通過上一篇 IoC 的例子,我們來認識一個 ApplicationContext 的子類——ClassPathXmlApplicationContext。
1.先在【src】目錄下創建一個 【bean.xml】 文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 通過 xml 方式裝配 bean --> <bean name="source" class="pojo.Source"> <property name="fruit" value="橙子"/> <property name="sugar" value="多糖"/> <property name="size" value="超大杯"/> </bean> </beans>
2.這裡定義了一個 bean ,這樣 Spring IoC 容器在初始化的時候就能找到它們,然後使用 ClassPathXmlApplicationContext 容器就可以將其初始化:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Source source = (Source) context.getBean("source", Source.class);
System.out.println(source.getFruit()); System.out.println(source.getSugar()); System.out.println(source.getSize());
這樣就會使用 Application 的實現類 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器,然後開發者就可以通過 IoC 容器來獲取資源了啦!
關於 Spring Bean 的裝配以及一些細節,會在下一篇文章中講到
1.ClassPathXmlApplicationContext: 讀取classpath中的資源
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2:FileSystemXmlApplicationContext:- 讀取指定路徑的資源
ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
3.XmlWebApplicationContext: 需要在Web的環境下才可以運行
XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 這時並沒有初始化容器 ac.setServletContext(servletContext); // 需要指定ServletContext對象 ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路徑,開頭的斜線表示Web應用的根目錄 ac.refresh(); // 初始化容器
雖然 Spring IoC 容器的生成十分的複雜,但是大體瞭解一下 Spring IoC 初始化的過程還是必要的。這對於理解 Spring 的一系列行為是很有幫助的。
注意:Bean 的定義和初始化在 Spring IoC 容器是兩大步驟,它是先定義,然後初始化和依賴注入的。
做完了以上 3 步,Bean 就在 Spring IoC 容器中被定義了,而沒有被初始化,更沒有完成依賴注入,也就是沒有注入其配置的資源給 Bean,那麼它還不能完全使用。
對於初始化和依賴注入,Spring Bean 還有一個配置選項——【lazy-init】,其含義就是是否初始化 Spring Bean。在沒有任何配置的情況下,它的默認值為 default,實際值為 false,也就是 Spring IoC 默認會自動初始化 Bean。如果將其設置為 true,那麼只有當我們使用 Spring IoC 容器的 getBean 方法獲取它時,它才會進行 Bean 的初始化,完成依賴注入。
最後我們簡單說說IoC是如何實現的。想像一下如果我們自己來實現這個依賴注入的功能,我們怎麼來做? 無外乎:
我們發現其實自己來實現也不是很難,Spring實際也就是這麼做的。這麼看的話其實IoC就是一個工廠模式的升級版!當然要做一個成熟的IoC框架,還是非常多細緻的工作要做,Spring不僅提供了一個已經成為業界標準的Java IoC框架,還提供了更多強大的功能,所以大家就別去造輪子啦!希望瞭解IoC更多實現細節不妨通過學習Spring的源碼來加深理解!
引用地址:這裡 【參考資料】:《Java EE 互聯網輕量級框架整合開發》、《Spring 實戰(第四版)》 【好文推薦】:①Spring 的本質系列(1) -- 依賴注入、 ②Spring的IoC原理
引用地址:這裡
歡迎轉載,轉載請註明出處!簡書ID:@我沒有三顆心臟github:wmyskxz歡迎關注公眾微信號:wmyskxz_javaweb分享自己的Java Web學習之路以及各種Java學習資料
歡迎轉載,轉載請註明出處!
推薦閱讀: