如何用比較快速的方法掌握Spring的核心——依賴注入,Java web輕量級開發面試教程 讀書筆記


      我們知道,Java方面的高級程序員一定得掌握Spring的技能,其中包括Spring 依賴注入(IOC),面向切面(AOP),和數據庫的整合(比如和Hibernate整合或聲明式事務等)以及Spring MVC架構。其中,Spring的依賴注入是重中之重,在面試時,面試官一定會問這方面的問題。

      根據我的培訓和面試經驗,這方面的知識點雖然不難(初學者估計最多3天就能看明白並調通程序),但要把其中的各混淆點(也就是面試點)講清楚不容易,換句話說,初級程序員在學習Spring IOC這部分的知識時,或多或少會走些彎路,在通常情況下,會經過一次或多次面試的失敗(就是交學費)后才能理順這部分的知識體系。

      在本文里,將整理歸納 java web輕量級開發面試教程 里的相關知識點,一次性地把這部分的知識點講全,可以這樣說,大家看完這篇文章后,能盡量減少交學費的次數。

      以上是開場白,如下是正文。

--------------------------------------------------------------------------------------------------------------------

        1 通過一個簡單但易學的案例來了解依賴注入

        步驟一 開發提供服務的SayHello.java程序。        

 1 package com;
 2 public class SayHello {
 3     private HelloWorldSpring helloWorldSpring;
 4     public HelloWorldSpring getHelloWorldSpring() {
 5         return helloWorldSpring;
 6     }
 7     public void setHelloWorldSpring(HelloWorldSpring helloWorldSpring) {
 8         this.helloWorldSpring = helloWorldSpring;
 9     }
10     public void sayHello(){
11         System.out.println("Say Hello:" + helloWorldSpring.sayHello());
12         }
13     }

       第10行定義了一個sayHello的方法,在這方法里,調用了在第3行定義的helloWorldSpring對象,輸出一串文字。

       這里有一個比較有意思的現象,雖然在第4行和第7行針對helloWorldSpring對象定義了get和set的方法,但在第11行使用helloWorldSpring對象之前,始終沒有用new關鍵字初始化這個對象,那么按照以往的經驗,會不會出現空指針異常呢?

        別着急,先看下HelloWorldSpring這個類里有沒有特殊的動作。

        步驟二 定義HelloWorldSpring.java這個類。

 

1 package com;
2 public class HelloWorldSpring {
3     private String sayContent;    
4     public String sayHello() {
5         System.out.println("HelloWorld Spring!");
6         return "Hello World Spring";
7     }
8 }

        這里直接在第4行定義了sayHello的方法,也沒看到特殊的代碼。接下來看一下在SpringMain這個類里是如何調用的。

        步驟三 開發調用者SpringMain.java,請大家注意一下調用sayHello方法的方式。

        

 1 package com;
 2 import org.springframework.context.ApplicationContext;
 3 import org.springframework.context.support.ClassPathXmlApplicationContext;
 4 public class SpringMain {
 5     public static void main(String[] args) {
 6         ApplicationContext context = new ClassPathXmlApplicationContext(
 7                 "conf/applicationContext-*.xml");
 8 
 9     SayHello sayHello = (SayHello) context.getBean("SayHello");
10     sayHello.sayHello();        
11     }
12 }

        關鍵代碼在第9行和第10行,是通過一個getBean方法,獲得了SayHello對象的一個實例,隨后調用了其中的sayHello方法。在這之前,通過第6行的代碼,裝載了一個配置文件。

       戲法變到這里,大家看到了兩個不可思議的地方。第一,在SayHello類里,始終沒有初始化HelloWorldSpring對象,就直接用了。第二,在SpringMain類里,沒有像往常那樣用SayHello sayHello = new SayHello();  的方法初始化對象,而是通過getBean的方式來獲得類的實例。

      其實,這里大家已經能看到“低耦合”的寫法了。讓我們最后看完Spring的配置文件再來綜合體驗IoC的好處。

      步驟四 編寫Spring的配置文件applicationContext-service-api.xml,關鍵代碼如下

      

1 <bean id="HelloWorldSpring" class="com.HelloWorldSpring">
2 </bean>
3 <bean id="SayHello" class="com.SayHello" >
4 <property name="helloWorldSpring" ref="HelloWorldSpring" />
5 </bean>

        在第1行和第2行里,定義了一個Bean。在Spring里,一個Bean往往和一個類所對應,這里id是HelloWorldSpring的這個Bean是和com.HelloWorldSpring這個類所對應。

        而在第3行到第5行里,用id是SayHello這個Bean對應com.SayHello這個類,請大家注意第4行,用內置property這個方式,把HelloWorldSpring這個類內嵌到SayHello類里。

       2 IoC的特點,不用New就可以初始化類

       SpringMain.java的主要代碼如下。

1     ApplicationContext context = new ClassPathXmlApplicationContext("conf/applicationContext-*.xml");

2     SayHello sayHello = (SayHello) context.getBean("SayHello");

3     sayHello.sayHello();       

       運行SpringMain.java時,首先是把配置文件里定義的信息裝載到context類里;接着在第2行里,通過context.getBean方法,根據配置文件的定義,獲取ID為SayHello(即class是com.SayHello)這個類;隨后在第3行里使用這個類的sayHello方法。從代碼中大家可以看出,這里同樣沒有用到new,而是根據配置文件來初始化類。

       沒有使用new,就意味着低耦合,具體而言,就是SayHello、HelloWorldSpring和SpringMain這三個類之間的耦合度很低。

      假設有三個團隊在開發維護這三個類,如果用常規new方法來創建類,比如在SayHello類里用HelloWorldSpring helloWorldSpring = new HelloWOrldSpring();,那么一旦管理HelloWorldSpring類的團隊要修改調用的接口,比如new的構造函數需要帶參數,那么SayHello類的管理者就不得不受無妄之災,也需要修改本身的代碼。

      要知道在公司里,修改代碼並且發布到生產環境,要經過很煩瑣且很嚴格的審批流程,必須要經歷代碼審查、代碼提交、測試人員測試、領導審批、最終發布以及發布后檢查這些步驟。如果用剛才看到的通過配置文件裝載類,在本地代碼里沒有new的這套開發方式,那么如果一個團隊修改了代碼,其他團隊就有可能不必改動代碼,這樣就可以很大程度上避免不必要的工作。

 

      3 控制翻轉和依賴注入

      控制翻轉的英文名字叫IoC(Inversion of Control),依賴注入英文名叫DI(Dependency Injection),下面通過下表來看一下它們的概念。

       

概念名

含義

表現形式

控制翻轉(IoC,控制反轉)

類之間的關系,不用代碼控制,而是由Spring容器(也就是Spring的jar包)來控制。控制權由代碼翻轉到容器里,這叫控制翻轉

在初始化對象時,在代碼里無須new,而是把類之間的關系寫到配置文件里

依賴注入(DI)

在代碼運行時,如果我們要在一個類里使用(也叫注入)另一個類,比如在上述的SayHello類里要初始化另外一個HelloWorldSpring類,那么這種注入就是依賴於配置文件的

同樣是把類之間的調用關系寫到配置文件里,在運行時,會根據配置文件,把HelloWorldSpring這個類注入SayHello里

        通過上面的描述,能看到它們其實是從不同的角度講述的同一件事情。依賴注入強調類的注入是由Spring容器在運行時完成,而控制反轉強調類之間的關系是由Spring容器控制。

       從這兩個名詞可知, Spring給我們帶來了一種全新的編程理念,即不用new也可以創建和使用對象。這種開發方式讓我們能像搭積木一樣組裝不同的類,組裝后的類之間的耦合度很低,一個類的修改可以不影響(或者影響度很小)其他的類,這樣就可以避免一個小修改帶來的一大串連鎖反應。

       大家在了解Spring的時候,一定請理解“低耦合”這個好處,這本來是面向對象思想帶給我們的好處,在Spring開發的過程中我們確實能感受到。

 

      4 讀取配置文件的各種方式

       在Spring里,通常在配置文件中描述各類以及類之間的包含關系,在使用的時候,會先加載配置文件,Spring的內核會讀取配置文件,隨后動態地組裝各類。

       通過下表來總結一下讀取配置文件的各種方式,它們之間沒有優劣之分,大家可以挑選個最適用的,具體來講,沒有特殊情況,就可以用ClassPathXmlApplicationContext。

 

類名

例子

XmlBeanFactory

Resource resource = new ClassPathResource("bean.xml"); 

BeanFactory factory = new XmlBeanFactory(resource); 

ClassPathXmlApplicationContext

ApplicationContext factory=new ClassPathXmlApplicationContext("conf/appcontext.xml"); 

用文件系統類來讀取

FileSystemXmlApplicationContext

ApplicationContext factory=new FileSystemXmlApplicationContext("classpath:appcontext.xml"); 

              5 單例和多例

        我們知道,Spring的容器會在程序運行時,根據配置文件自動地創建(或者叫實例化)具體的Java類(也叫class,或叫Bean)。在配置文件里,可以設置創建文件時是否用單例的方式,如果沒有設置,則會自動用默認的單例的方式來創建文件。如果不想用單例,則可以通過如下兩種語法來修改,它們是等價的。

        <bean id="SayHello" class="com.SayHello" singleton="false"> 或者

         <bean id="SayHello" class="com.SayHello" scope="prototype">

       在實際項目中,一般用單例模式來創建無狀態的Bean,而對於有狀態的Bean,一般不用這種模式。所謂無狀態的Bean,是指沒有能夠標識它目前狀態屬性的Bean,比如共享單車,A用好以后,可以放入共享池(即放到馬路邊上),B可以繼續使用。由於沒有供某個特定的用戶使用,所以也就不能保持某一用戶的狀態,所以叫無狀態Bean。相反,如果針對個人的自行車,那么會有個狀態來表明是個人的。

      講到這里,請大家確認如下概念,並不是我們首先設置了singleton是false,所以Spring容器才用單例的方式,恰恰相反,根據實際的需求,待創建的類可以被其他多個類共享,因此我們才設置singleton是false。是先有需求再有具體的實現。

    這個知識點可以說是Spring面試的必考點,下面通過下表來對比一下兩者的差別

     

列別

實際用例

特點

有狀態Bean

我們訪問網站登錄后都有自己的用戶名和密碼,系統可以用一個有狀態的Bean來記錄我們的訪問信息,比如來源IP訪問頁面列表和訪問時間等

會為每次調用創建一個實例,一旦調用結束,比如用戶離開了網站,則該Bean就會被銷毀

無狀態Bean

數據庫連接的通用類,其他類可以用它來獲取數據庫連接並進行操作

可以在緩沖池里只維護一個實例,無須創建和銷毀操作,性能高,但是線程不安全

 

 

  6 論面試

        當年我追過一本小說,叫天擇,里面有個故事情節,皇帝請主角吃飯,讓主角點菜,主角點的不是龍肝鳳膽,也不是山珍海味,是兩個家常菜,炒青菜和蛋炒飯,如下我引用的是書中原話:

       天下萬事萬物,都是有一個從簡單到復雜,又從復雜趨向於簡單的過程,用道家的話來說,就是天下大道,以簡馭繁,用佛家的話來說,就是看山是山,看山不是山,看山仍是山的三大境界。蛋炒飯和炒青菜這兩樣東西每個人都吃了不知道多少次,但是,正因為如此,能夠在這平凡當中做出來令人難忘的偉大味道,這才是頂尖的高手的境界!

     學習Spring IOC這平凡的知識點,也會經歷過上述”從簡單到復雜“的過程,作者根據多年面試培訓(甚至包括寫書)的經驗,從紛繁復雜的Spring IOC的諸多知識點中提煉出針對初級程序員有用的上述內容,不能說是頂尖高手,但至少也經過沉淀,對大家多少有些幫助,也一定能幫助大家少走些彎路(這也是本文申請加入首頁的理由)。

      面試時也這樣,面試官會在乎候選人掌握多少知識點(廣度),更在乎對於知識點的深度,如果候選人能從IOC這種平凡的知識點里說出自己的高深體會,這樣反而能更打動面試官。

        在這方面可以說出如下的要點:

      1 基本概念(誰都會說)

      2 結合項目說明怎么用IOC,以及IOC的好處(不用new就能用,低耦合),這大家可以結合本文里提到的案例說明

      3 一些外圍的知識點,比如如何導入配置文件

      4 特別地,請講述單例和多例,並請結合具體例子說明在項目里的用法。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM