Spring框架由Rod Johnson開發,2004年發布了Spring框架的第一版。Spring是一個從實際開發中抽取出來的框架,因此它完成了大量開發中的通用步驟,留給開發者的僅僅是與特定應用相關的部分,從而大大提高了企業應用的開發效率。
Spring總結起來優點如下:
低侵入式設計,代碼的污染極低
獨立於各種應用服務器,基於Spring框架的應用,可以真正實現Write Once,Run Anywhere的承諾
Spring的IoC容器降低了業務對象替換的復雜性,提高了組件之間的解耦
Spring的AOP支持允許將一些通用任務如安全、事務、日志等進行集中式管理,從而提供了更好的復用
Spring的ORM和DAO提供了與第三方持久層框架的良好整合,並簡化了底層的數據庫訪問
Spring的高度開放性,並不強制應用完全依賴於Spring,開發者可自由選用Spring框架的部分或全部
Spring框架的組成結構圖如下所示
spring-overview
程序主要是通過Spring容器來訪問容器中的Bean,ApplicationContext是Spring容器最常用的接口,該接口有如下兩個實現類
ClassPathXmlApplicationContext: 從類加載路徑下搜索配置文件,並根據配置文件來創建Spring容器
FileSystemXmlApplicationContext: 從文件系統的相對路徑或絕對路徑下去搜索配置文件,並根據配置文件來創建Spring容器
1 2 3 4 5 6 7
public class BeanTest{ public static void main(String args[]) throws Exception{ ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); Person p = ctx.getBean("person", Person.class); p.say(); } }
在Eclipse等IDE工具中,用戶可以自建User Library,然後把Spring的Jar包都放入其中,當然也可以將Jar包直接放在項目的/WEB-INF/lib目錄下,但是如果使用User Library,在項目發布時,需要將用戶庫所引用的Jar文件隨應用一起發布,就是將User Library所使用的Jar復制到/WEB-INF/lib目錄下,這是因為對於一個Web應用,Eclipse部署Web應用時不會將用戶庫的Jar文件復制到/WEB-INF/lib下,需要手動復制。
Spring框架的核心功能有兩個
Spring容器作為超級大工廠,負責創建、管理所有的Java對象,這些Java對象被稱為Bean
Spring容器管理容器中Bean之間的依賴關系,Spring使用一種被稱為“依賴注入”的方式來管理Bean之間的依賴關系
使用依賴注入,不僅可以為Bean注入普通的屬性值,還可以注入其他Bean的引用。依賴注入是一種優秀的解耦方式,其可以讓Bean以配置文件組織在一起,而不是以硬編碼的方式耦合在一起。
Rod Johnson是第一個高度重視以配置文件來管理Java實例的協作關系的人,他給這種方式起了一個名字:控制反轉(Inverse of Control,IoC)。後來Martine Fowler為這種方式起了另一個名稱:依賴注入(Dependency Injection),因此不管是依賴注入,還是控制反轉,其含義完全相同。當某個Java對象(調用者)需要調用另一個Java對象(被依賴對象)的方法時,在傳統模式下通常有兩種做法
原始做法: 調用者主動創建被依賴對象,然後再調用被依賴對象的方法
簡單工廠模式: 調用者先找到被依賴對象的工廠,然後主動通過工廠去獲取被依賴對象,最後再調用被依賴對象的方法
注意上面的主動二字,這必然會導致調用者與被依賴對象實現類的硬編碼耦合,非常不利於項目升級的維護。使用Spring框架之後,調用者無需主動獲取被依賴對象,調用者只要被動接受Spring容器為調用者的成員變量賦值即可,由此可見,使用Spring後,調用者獲取被依賴對象的方式由原來的主動獲取,變成了被動接受——所以Rod Johnson稱之為控制反轉。
另外從Spring容器的角度來看,Spring容器負責將被依賴對象賦值給調用者的成員變量——相當於為調用者注入它依賴的實例,因此Martine Fowler稱之為依賴注入。
設值注入是指IoC容器通過成員變量的setter方法來注入被依賴對象。這種注入方式簡單、直觀,因而在Spring的依賴注入裡大量使用。
利用構造器來設置依賴關系的方式,被稱為構造注入。通俗來說,就是驅動Spring在底層以反射方式執行帶指定參數的構造器,當執行帶參數的構造器時,就可利用構造器參數對成員變量執行初始化——這就是構造注入的本質。
設值注入有如下優點
與傳統的JavaBean的寫法更相似,程序開發人員更容易理解、接受。通過setter方法設定依賴關系顯得更加直觀、自然
對於復雜的依賴關系,如果采用構造注入,會導致構造器過於臃腫,難以閱讀。Spring在創建Bean實例時,需要同時實例化其依賴的全部實例,因而導致性能下降。而使用設值注入,則能避免這些問題。
尤其在某些成員變量可選的情況下,多參數的構造器更加笨重
構造注入優勢如下
構造注入可以在構造器中決定依賴關系的注入順序,優先依賴的優先注入
對於依賴關系無需變化的Bean,構造注入更有用處。因為沒有setter方法,所有的依賴關系全部在構造器內設定,無須擔心後續的代碼對依賴關系產生破壞
依賴關系只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關系,對組件的調用者而言,組件內部的依賴關系完全透明,更符合高內聚的原則
Notes
建議采用設值注入為主,構造注入為輔的注入策略。對於依賴關系無須變化的注入,盡量采用構造注入;而其他依賴關系的注入,則考慮采用設值注入。
對於開發者來說,開發者使用Spring框架主要是做兩件事:①開發Bean;②配置Bean。對於Spring框架來說,它要做的就是根據配置文件來創建Bean實例,並調用Bean實例的方法完成“依賴注入”——這就是所謂IoC的本質。
當通過Spring容器創建一個Bean實例時,不僅可以完成Bean實例的實例化,還可以為Bean指定特定的作用域。Spring支持如下五種作用域
singleton: 單例模式,在整個Spring IoC容器中,singleton作用域的Bean將只生成一個實例
prototype: 每次通過容器的getBean()方法獲取prototype作用域的Bean時,都將產生一個新的Bean實例
request: 對於一次HTTP請求,request作用域的Bean將只生成一個實例,這意味著,在同一次HTTP請求內,程序每次請求該Bean,得到的總是同一個實例。只有在Web應用中使用Spring時,該作用域才真正有效
對於一次HTTP會話,session作用域的Bean將只生成一個實例,這意味著,在同一次HTTP會話內,程序每次請求該Bean,得到的總是同一個實例。只有在Web應用中使用Spring時,該作用域才真正有效
global session: 每個全局的HTTP Session對應一個Bean實例。在典型的情況下,僅在使用portlet context的時候有效,同樣只在Web應用中有效
如果不指定Bean的作用域,Spring默認使用singleton作用域。prototype作用域的Bean的創建、銷毀代價比較大。而singleton作用域的Bean實例一旦創建成果,就可以重復使用。因此,應該盡量避免將Bean設置成prototype作用域。
Spring能自動裝配Bean與Bean之間的依賴關系,即無須使用ref顯式指定依賴Bean,而是由Spring容器檢查XML配置文件內容,根據某種規則,為調用者Bean注入被依賴的Bean。
Spring自動裝配可通過<beans/>元素的default-autowire屬性指定,該屬性對配置文件中所有的Bean起作用;也可通過對<bean/>元素的autowire屬性指定,該屬性只對該Bean起作用。
autowire和default-autowire可以接受如下值
no: 不使用自動裝配。Bean依賴必須通過ref元素定義。這是默認配置,在較大的部署環境中不鼓勵改變這個配置,顯式配置合作者能夠得到更清晰的依賴關系
byName: 根據setter方法名進行自動裝配。Spring容器查找容器中全部Bean,找出其id與setter方法名去掉set前綴,並小寫首字母後同名的Bean來完成注入。如果沒有找到匹配的Bean實例,則Spring不會進行任何注入
byType: 根據setter方法的形參類型來自動裝配。Spring容器查找容器中的全部Bean,如果正好有一個Bean類型與setter方法的形參類型匹配,就自動注入這個Bean;如果找到多個這樣的Bean,就拋出一個異常;如果沒有找到這樣的Bean,則什麼都不會發生,setter方法不會被調用
constructor: 與byType類似,區別是用於自動匹配構造器的參數。如果容器不能恰好找到一個與構造器參數類型匹配的Bean,則會拋出一個異常
autodetect: Spring容器根據Bean內部結構,自行決定使用constructor或byType策略。如果找到一個默認的構造函數,那麼就會應用byType策略
當一個Bean既使用自動裝配依賴,又使用ref顯式指定依賴時,則顯式指定的依賴覆蓋自動裝配依賴;對於大型的應用,不鼓勵使用自動裝配。雖然使用自動裝配可減少配置文件的工作量,但大大將死了依賴關系的清晰性和透明性。依賴關系的裝配依賴於源文件的屬性名和屬性類型,導致Bean與Bean之間的耦合降低到代碼層次,不利於高層次解耦
1 2 3 4
<!--通過設置可以將Bean排除在自動裝配之外--> <bean id="" autowire-candidate="false"/> <!--除此之外,還可以在beans元素中指定,支持模式字符串,如下所有以abc結尾的Bean都被排除在自動裝配之外--> <beans default-autowire-candidates="*abc"/>