Spring IOC知識點一網打盡!

前言

只有光頭才能變強html

回顧前面:java

在刷Spring書籍的時候花了點時間去學習了單例模式和工廠模式,總的來講仍是很是值得的!web

原本想的是刷完《Spring 實戰 (第4版)》和《精通Spring4.x 企業應用開發實戰》的IOC章節後來從新編寫一篇IOC的文章的,看了一下以前已經寫過的入門系列Spring入門這一篇就夠了Spring【依賴注入】就是這麼簡單。最主要的知識點都已經講過了,因此感受就不必從新來編寫這些知識點了...面試

我我的又不喜歡將寫過的東西複製到新的文章中,因此建議你們能夠先去閱讀上面兩篇文章再來看這篇(工廠模式那篇若是沒有看過的同窗也有必要去看看)~~spring

  • 爲了這篇文章知識點的完整性,重要的知識點(IOC概念理解,建立Bean、注入的三種方式等)仍是會出現,可是不會將上面兩篇博文的代碼摘抄過來了。

這篇文章主要是補充和強化一些比較重要的知識點,並會把上面的兩本書關於IOC的知識點整理出來並畫成一個思惟導圖來全面瞭解Spring IOC的知識點!數據庫

那麼接下來就開始吧,若是有錯的地方但願能多多包涵,並不吝在評論區指正!編程

1、Spring IOC全面認知

結合《Spring 實戰 (第4版)》和《精通Spring4.x 企業應用開發實戰》兩本書的IOC章節將其知識點整理起來~設計模式

1.1IOC和DI概述

在《精通Spring4.x 企業應用開發實戰》中對IOC的定義是這樣的:緩存

IoC(Inversion of Control)控制反轉,包含了兩個方面:1、控制。2、反轉安全

咱們能夠簡單認爲:

  • 控制指的是:當前對象對內部成員的控制權
  • 反轉指的是:這種控制權不禁當前對象管理了,由其餘(類,第三方容器)來管理。

IOC不夠開門見山,因而Martin Fowler提出了DI(dependency injection)來替代IoC,即讓調用類對某一接口實現類的依賴關係由第三方(容器或協做類)注入,以移除調用類對某一接口實現類的依賴。

在《Spring 實戰 (第4版)》中並無說起到IOC,而是直接來講DI的:

經過DI,對象的依賴關係將由系統中負責協調各對象的第三方組件在建立對象的時候進行設定,對象無需自行建立或管理它們的依賴關係,依賴關係將被自動注入到須要它們的對象當中去

從書上咱們也能夠發現:IoC和DI的定義(區別)並非如此容易就能夠說得清楚的了。這裏我就簡單摘抄一下:

  • IoC(思想,設計模式)主要的實現方式有兩種:依賴查找,依賴注入
  • 依賴注入是一種更可取的方式(實現的方式)

對咱們而言,其實也不必分得那麼清,混合一談也不影響咱們的理解...

再經過昨天寫過的工廠模式理解了沒有?,咱們如今就能夠很清楚的發現,其實所謂的IOC容器就是一個大工廠【第三方容器】(Spring實現的功能很強大!比咱們本身手寫的工廠要好不少)。

使用IOC的好處(知乎@Intopass的回答):

  1. 不用本身組裝,拿來就用。
  2. 享受單例的好處,效率高,不浪費空間。
  3. 便於單元測試,方便切換mock組件。
  4. 便於進行AOP操做,對於使用者是透明的。
  5. 統一配置,便於修改。

參考資料:

1.2IOC容器的原理

從上面就已經說了:IOC容器其實就是一個大工廠,它用來管理咱們全部的對象以及依賴關係。

  • 原理就是經過Java的反射技術來實現的!經過反射咱們能夠獲取類的全部信息(成員變量、類名等等等)!
  • 再經過配置文件(xml)或者註解來描述類與類之間的關係
  • 咱們就能夠經過這些配置信息和反射技術來構建出對應的對象和依賴關係了!

上面描述的技術只要學過點Java的都能說出來,這一會兒可能就會被面試官問倒了,咱們簡單來看看實際Spring IOC容器是怎麼實現對象的建立和依賴的:

 

 

  1. 根據Bean配置信息在容器內部建立Bean定義註冊表
  2. 根據註冊表加載、實例化bean、創建Bean與Bean之間的依賴關係
  3. 將這些準備就緒的Bean放到Map緩存池中,等待應用程序調用

Spring容器(Bean工廠)可簡單分紅兩種:

  • BeanFactory
    • 這是最基礎、面向Spring的
  • ApplicationContext
    • 這是在BeanFactory基礎之上,面向使用Spring框架的開發者。提供了一系列的功能!

幾乎全部的應用場合都是使用ApplicationContext!

BeanFactory的繼承體系:

 

 

ApplicationContext的繼承體系:

 

 

其中在ApplicationContext子類中又有一個比較重要的:WebApplicationContext

  • 專門爲Web應用準備的

 

 

Web應用與Spring融合:

 

 

咱們看看BeanFactory的生命週期:

 

 

接下來咱們再看看ApplicationContext的生命週期:

 

 

初始化的過程都是比較長,咱們能夠分類來對其進行解析:

  • Bean自身的方法:如調用 Bean 構造函數實例化 Bean,調用 Setter 設置 Bean 的屬性值以及經過的 init-method 和 destroy-method 所指定的方法;
  • Bean級生命週期接口方法:如 BeanNameAware、 BeanFactoryAware、 InitializingBean 和 DisposableBean,這些接口方法由 Bean 類直接實現;
  • 容器級生命週期接口方法:在上圖中帶「★」 的步驟是由 InstantiationAwareBean PostProcessor 和 BeanPostProcessor 這兩個接口實現,通常稱它們的實現類爲「 後處理器」 。 後處理器接口通常不禁 Bean 自己實現,它們獨立於 Bean,實現類以容器附加裝置的形式註冊到Spring容器中並經過接口反射爲Spring容器預先識別。當Spring 容器建立任何 Bean 的時候,這些後處理器都會發生做用,因此這些後處理器的影響是全局性的。固然,用戶能夠經過合理地編寫後處理器,讓其僅對感興趣Bean 進行加工處理

ApplicationContext和BeanFactory不一樣之處在於:

  • ApplicationContext會利用Java反射機制自動識別出配置文件中定義的BeanPostProcessor、 InstantiationAwareBeanPostProcesso 和BeanFactoryPostProcessor後置器,並自動將它們註冊到應用上下文中。而BeanFactory須要在代碼中經過手工調用addBeanPostProcessor()方法進行註冊
  • ApplicationContext在初始化應用上下文的時候就實例化全部單實例的Bean。而BeanFactory在初始化容器的時候並未實例化Bean,直到第一次訪問某個Bean時實例化目標Bean。

有了上面的知識點了,咱們再來詳細地看看Bean的初始化過程:

 

 

簡要總結:

  • BeanDefinitionReader讀取Resource所指向的配置文件資源,而後解析配置文件。配置文件中每個<bean>解析成一個BeanDefinition對象,並保存到BeanDefinitionRegistry中;
  • 容器掃描BeanDefinitionRegistry中的BeanDefinition;調用InstantiationStrategy進行Bean實例化的工做;使用BeanWrapper完成Bean屬性的設置工做;
  • 單例Bean緩存池:Spring 在DefaultSingletonBeanRegistry類中提供了一個用於緩存單實例 Bean 的緩存器,它是一個用HashMap實現的緩存器,單實例的Bean以beanName爲鍵保存在這個HashMap中。

1.3IOC容器裝配Bean

1.3.1裝配Bean方式

Spring4.x開始IOC容器裝配Bean有4種方式:

  • XML配置
  • 註解
  • JavaConfig
  • 基於Groovy DSL配置(這種不多見)

總的來講:咱們以XML配置+註解來裝配Bean得多,其中註解這種方式佔大部分

1.3.2依賴注入方式

依賴注入的方式有3種方式:

  • 屬性注入-->經過setter()方法注入
  • 構造函數注入
  • 工廠方法注入

總的來講使用屬性注入是比較靈活和方便的,這是大多數人的選擇!

1.3.3對象之間關係

<bean>對象之間有三種關係:

  • 依賴-->挺少用的(使用depends-on就是依賴關係了-->前置依賴【依賴的Bean須要初始化以後,當前Bean纔會初始化】)
  • 繼承-->可能會用到(指定abstract和parent來實現繼承關係)
  • 引用-->最多見(使用ref就是引用關係了)

1.3.4Bean的做用域

Bean的做用域:

  • 單例Singleton
  • 多例prototype
  • 與Web應用環境相關的Bean做用域
    • reqeust
    • session

使用到了Web應用環境相關的Bean做用域的話,是須要咱們手動配置代理的~

 

 

緣由也很簡單:由於咱們默認的Bean是單例的,爲了適配Web應用環境相關的Bean做用域--->每一個request都須要一個對象,此時咱們返回一個代理對象出去就能夠完成咱們的需求了!

 

 

將Bean配置單例的時候還有一個問題:

  • 若是咱們的Bean配置的是單例,而Bean對象裏邊的成員對象咱們但願是多例的話。那怎麼辦呢??
  • 默認的狀況下咱們的Bean單例,返回的成員對象也默認是單例的(由於對象就只有那麼一個)!

此時咱們須要用到了lookup方法注入,使用也很簡單,看看例子就明白了:

 

 

1.3.6處理自動裝配的歧義性

昨天在刷書的時候恰好看到了有人在知乎邀請我回答這個問題:

 

 

結合兩本書的知識點,能夠概括成兩種解決方案:

  • 使用@Primary註解設置爲首選的注入Bean
  • 使用@Qualifier註解設置特定名稱的Bean來限定注入!
    • 也可使用自定義的註解來標識

1.3.7引用屬性文件以及Bean屬性

以前在寫配置文件的時候都是直接將咱們的數據庫配置信息在裏面寫死的了:

 

 

其實咱們有更優雅的作法:將這些配置信息寫到配置文件上(由於這些配置信息極可能是會變的,並且有可能被多個配置文件引用).

  • 如此一來,咱們改的時候就十分方便了。

 

 

引用配置文件的數據使用的是${}

除了引用配置文件上的數據,咱們還能夠引用Bean的屬性

 

 

 

 

引用Bean的屬性使用的是#{}

在這種技術在《Spring 實戰 第四版》稱之爲Spring EL,跟咱們以前學過的EL表達式是相似的。主要的功能就是上面的那種,想要更深刻了解可參考下面的連接:

1.3.8組合配置文件

xml文件之間組合:

 

 

xml和javaconfig互相組合的方式:

public static void main(String[] args) {
		
        //1.經過構造函數加載配置類
         ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);

        //2.經過編碼方式註冊配置類
		 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		 ctx.register(DaoConfig.class);
		 ctx.register(ServiceConfig.class);
		 ctx.refresh();

        //3.經過XML組裝@Configuration配置類所提供的配置信息
		 ApplicationContext ctx = new ClassPathXmlApplicationContext("com/smart/conf/beans2.xml");

        //4.經過@Configuration組裝XML配置所提供的配置信息
		 ApplicationContext ctx = new AnnotationConfigApplicationContext(LogonAppConfig.class);

		 //5.@Configuration的配置類相互引用
		 ApplicationContext ctx = new AnnotationConfigApplicationContext(DaoConfig.class,ServiceConfig.class);
         LogonService logonService = ctx.getBean(LogonService.class);
         System.out.println((logonService.getLogDao() !=null));
         logonService.printHelllo();   
	}

第一種的例子:

 

 

第二種的例子:

 

 

 

 

第三種的例子:

 

 

第四種的例子:

 

 

第五種的例子:

  • 代碼由上可見

1.3.9裝配Bean總結

總的來講,Spring IOC容器就是在建立Bean的時候有不少的方式給了咱們實現,其中也包括了不少關於Bean的配置~

對於Bean相關的注入教程代碼和簡化配置(p和c名稱空間)我就不一一說明啦,大家去看Spring入門這一篇就夠了Spring【依賴注入】就是這麼簡單就好了。

總的對比圖:

 

 

分別的應用場景:

 

 

至於一些小的知識點:

  • 方法替換
    • 使用某個Bean的方法替換成另外一個Bean的方法
  • 屬性編輯器
    • Spring能夠對基本類型作轉換就歸結於屬性編輯器的功勞!
  • 國際化
    • 使用不一樣語言(英語、中文)的操做系統去顯式不一樣的語言
  • profile與條件化的Bean
    • 知足了某個條件才初始化Bean,這能夠方便切換生產環境和開發環境~
  • 容器事件
    • 相似於咱們的Servlet的監聽器,只不過它是在Spring中實現了~

上面這些小知識點比較少狀況會用到,這也不去講解啦。知道有這麼一回事,到時候查查就會用啦~~~

2、Spring IOC相關面試題

將SpringIOC相關知識點整理了一遍,要想知道哪些知識點是比較重要的。很簡單,咱們去找找相關的面試題就知道了,若是該面試題是常見的,那麼說明這個知識點仍是相對比較重要的啦!

如下的面試題從各類博客上摘抄下來,摘抄量較大的會註明出處的~

2.1什麼是spring?

什麼是spring?

Spring 是個java企業級應用的開源開發框架。Spring主要用來開發Java應用,可是有些擴展是針對構建J2EE平臺的web應用。Spring框架目標是簡化Java企業級應用開發,並經過POJO爲基礎的編程模型促進良好的編程習慣。

2.2使用Spring框架的好處是什麼?

使用Spring框架的好處是什麼?

  • 輕量:Spring 是輕量的,基本的版本大約2MB。
  • 控制反轉:Spring經過控制反轉實現了鬆散耦合,對象們給出它們的依賴,而不是建立或查找依賴的對象們。
  • 面向切面的編程(AOP):Spring支持面向切面的編程,而且把應用業務邏輯和系統服務分開。
  • 容器:Spring 包含並管理應用中對象的生命週期和配置。
  • MVC框架:Spring的WEB框架是個精心設計的框架,是Web框架的一個很好的替代品。
  • 事務管理:Spring 提供一個持續的事務管理接口,能夠擴展到上至本地事務下至全局事務(JTA)。
  • 異常處理:Spring 提供方便的API把具體技術相關的異常(好比由JDBC,Hibernate or JDO拋出的)轉化爲一致的unchecked 異常。

2.3Spring由哪些模塊組成?

Spring由哪些模塊組成?

簡單能夠分紅6大模塊:

  • Core
  • AOP
  • ORM
  • DAO
  • Web
  • Spring EE

 

 

2.4BeanFactory 實現舉例

BeanFactory 實現舉例

Bean工廠是工廠模式的一個實現,提供了控制反轉功能,用來把應用的配置和依賴從正真的應用代碼中分離

在spring3.2以前最經常使用的是XmlBeanFactory的,但如今被廢棄了,取而代之的是:XmlBeanDefinitionReader和DefaultListableBeanFactory

2.5什麼是Spring的依賴注入?

什麼是Spring的依賴注入?

依賴注入,是IOC的一個方面,是個一般的概念,它有多種解釋。這概念是說你不用建立對象,而只須要描述它如何被建立。你不在代碼裏直接組裝你的組件和服務,可是要在配置文件裏描述哪些組件須要哪些服務,以後一個容器(IOC容器)負責把他們組裝起來。

2.6有哪些不一樣類型的IOC(依賴注入)方式?

有哪些不一樣類型的IOC(依賴注入)方式?

  • 構造器依賴注入:構造器依賴注入經過容器觸發一個類的構造器來實現的,該類有一系列參數,每一個參數表明一個對其餘類的依賴。
  • Setter方法注入:Setter方法注入是容器經過調用無參構造器或無參static工廠 方法實例化bean以後,調用該bean的setter方法,即實現了基於setter的依賴注入。
  • 工廠注入:這個是遺留下來的,不多用的了!

2.7哪一種依賴注入方式你建議使用,構造器注入,仍是 Setter方法注入?

哪一種依賴注入方式你建議使用,構造器注入,仍是 Setter方法注入?

你兩種依賴方式均可以使用,構造器注入和Setter方法注入。最好的解決方案是用構造器參數實現強制依賴,setter方法實現可選依賴

2.8什麼是Spring beans?

什麼是Spring beans?

Spring beans 是那些造成Spring應用的主幹的java對象。它們被Spring IOC容器初始化,裝配,和管理。這些beans經過容器中配置的元數據建立。好比,以XML文件中<bean/>的形式定義。

這裏有四種重要的方法給Spring容器提供配置元數據

  • XML配置文件。
  • 基於註解的配置。
  • 基於java的配置。
  • Groovy DSL配置

2.9解釋Spring框架中bean的生命週期

解釋Spring框架中bean的生命週期

  • Spring容器 從XML 文件中讀取bean的定義,並實例化bean。
  • Spring根據bean的定義填充全部的屬性。
  • 若是bean實現了BeanNameAware 接口,Spring 傳遞bean 的ID 到 setBeanName方法。
  • 若是Bean 實現了 BeanFactoryAware 接口, Spring傳遞beanfactory 給setBeanFactory 方法。
  • 若是有任何與bean相關聯的BeanPostProcessors,Spring會在postProcesserBeforeInitialization()方法內調用它們。
  • 若是bean實現IntializingBean了,調用它的afterPropertySet方法,若是bean聲明瞭初始化方法,調用此初始化方法。
  • 若是有BeanPostProcessors 和bean 關聯,這些bean的postProcessAfterInitialization() 方法將被調用。
  • 若是bean實現了 DisposableBean,它將調用destroy()方法。

2.10解釋不一樣方式的自動裝配

解釋不一樣方式的自動裝配

  • no:默認的方式是不進行自動裝配,經過顯式設置ref 屬性來進行裝配。
  • byName:經過參數名 自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byname,以後容器試圖匹配、裝配和該bean的屬性具備相同名字的bean。
  • byType::經過參數類型自動裝配,Spring容器在配置文件中發現bean的autowire屬性被設置成byType,以後容器試圖匹配、裝配和該bean的屬性具備相同類型的bean。若是有多個bean符合條件,則拋出錯誤。
  • constructor:這個方式相似於byType, 可是要提供給構造器參數,若是沒有肯定的帶參數的構造器參數類型,將會拋出異常。
  • autodetect:首先嚐試使用constructor來自動裝配,若是沒法工做,則使用byType方式。

只用註解的方式時,註解默認是使用byType的

2.11IOC的優勢是什麼?

IOC的優勢是什麼?

IOC 或 依賴注入把應用的代碼量降到最低。它使應用容易測試,單元測試再也不須要單例和JNDI查找機制。最小的代價和最小的侵入性使鬆散耦合得以實現。IOC容器支持加載服務時的餓漢式初始化和懶加載

2.12哪些是重要的bean生命週期方法? 你能重載它們嗎?

哪些是重要的bean生命週期方法? 你能重載它們嗎?

有兩個重要的bean 生命週期方法,第一個是setup, 它是在容器加載bean的時候被調用。第二個方法是 teardown 它是在容器卸載類的時候被調用。

The bean 標籤有兩個重要的屬性(init-methoddestroy-method)。用它們你能夠本身定製初始化和註銷方法。它們也有相應的註解(@PostConstruct@PreDestroy)。

2.13怎麼回答面試官:你對Spring的理解?

怎麼回答面試官:你對Spring的理解?

來源:

下面我就截幾個答案:

1、

 

 

2、

 

 

2.14Spring框架中的單例Beans是線程安全的麼?

Spring框架中的單例Beans是線程安全的麼?

Spring框架並無對單例bean進行任何多線程的封裝處理。關於單例bean的線程安全和併發問題須要開發者自行去搞定。但實際上,大部分的Spring bean並無可變的狀態(好比Serview類和DAO類),因此在某種程度上說Spring的單例bean是線程安全的。若是你的bean有多種狀態的話(好比 View Model 對象),就須要自行保證線程安全

最淺顯的解決辦法就是將多態bean的做用域由「singleton」變動爲「prototype」

2.15FileSystemResource和ClassPathResource有何區別?

FileSystemResource和ClassPathResource有何區別?

在FileSystemResource 中須要給出spring-config.xml文件在你項目中的相對路徑或者絕對路徑。在ClassPathResource中spring會在ClassPath中自動搜尋配置文件,因此要把ClassPathResource文件放在ClassPath下。

若是將spring-config.xml保存在了src文件夾下的話,只需給出配置文件的名稱便可,由於src文件夾是默認。

簡而言之,ClassPathResource在環境變量中讀取配置文件,FileSystemResource在配置文件中讀取配置文件

3、總結

這篇文章的主要知識點我畫了一張思惟導圖來總結啦,當學習到AOP的時候,這張思惟導圖會繼續補充的哦~~~

 

 

參考資料:

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y

文章的目錄導航

相關文章
相關標籤/搜索