天天看點

spring單例、多例使用方法

今天聊聊單例和多例。隻想看spring管理的執行個體有哪些模式,直接看最後。

相信大部分使用java 做web開發的開發人員都用過spring。spring功能最基礎功能就是IoC(Inversion of control——控制反轉)、AOP(Aspect Oriented Programming——面向切面程式設計)。其中IoC核心是DI(Dependency injection——依賴注入)。

我們最開始寫項目自然而然的是沒有架構,生寫!但代碼多了之後,發現有很多代碼,可以抽成公共方法。有些又可以抽成一個類。而有些類又是貫穿整個項目生命周期始終的,而且往往這些類的初始化方法很複雜且重要。那怎麼辦,總不能每次使用的時候初始化一遍吧,這樣很耗編碼時間不說,還很占用計算機性能。于是,工廠模式應運而生。通過工廠模式擷取各個重要的執行個體對象。這樣就帶來一個問題,怎樣保證執行個體隻建立一次呢?單例模式應運而生。于是,我們常用的架構spring就成了。

然而需求的發展往往不是單一技術能很好解決的。單例、依賴注入固然好。但是也讓我們的開發模式陷入一種定式。即controller、service、dao這樣雖然是快速規範的劃分,但是往往一些複雜的邏輯隻在service或者controller中寫會有大量的私有方法、或者一個方法幾百上千行。整個業務操作的生命周期局限在一個方法内。并不能好好利用面向對象的思想,寫到最後完全就是面向過程程式設計。一旦邏輯複雜,那方法寫的簡直慘不忍睹,而且局限于方法的生命周期,很多參數可能會多次調用資料庫查同一個資料。那麼有什麼辦法能改變這個局面呢?曆史總會給我們答案。

自新世紀之初提出“領域驅動設計”(DDD)以來,這玩意一直不受重視,不僅玄之又玄的理論很少有人去專研,而且所謂“靈活開發”的盛行,也不适合DDD。但這裡并不介紹DDD,說一說DDD的充血模型要在傳統資料驅動的業務中使用将面臨的首要問題——單例如何注入進充血模型。

比如,我有個User對象,而對象的儲存查詢操作是與資料庫操作。我并不想讓User是一個幹癟的值對象,而是讓他具備行為,是一個真正有血有肉的充血模型。那麼saveUser(User)這樣的方法就不再由Dao提供,而是應該由user.save()替代。熟悉JPA的同學肯定想到了。jpa支援對象操作替代傳統的repository操作。例如典型的user中的List<Role> roles屬性作為關聯查詢的屬性。如果設定級聯查詢為懶加載,那麼jpa隻在調用user.getRoles()方法執行的時候發送sql查詢對應的role。這是因為jpa代理了user實體對象,而且這也有個問題。如果被jpa代理的對象調用toString()方法,擷取roles屬性列印時會觸發jpa操作。但這時可能已經不屬于jpa Entity的生命周期了。踩過坑的朋友肯定遇到過列印日志報了莫名的jpa異常,百思不得其解吧。

況且我們想要的不隻是這些。我們可能有些其他的被sqring單例管理的對象方法需要在不同的執行個體對象中使用。例如:user想要發送資料到遠端。那麼user.send()可就不歸jpa管了。此時如果要想讓user能做這個事情必然隻能通過spring上下文擷取被spring管理的類。聰明的小夥肯定想到了。我讓user也被spring管理起來,不就可以注入了嗎?是的,但是一旦被spring管理預設就是單例的。總不能每個user都是同一個吧。其實spring可以設定多例的,隻是用的人很少。在加有@componet之類注解(@service、@bean...等)受spring管理的類上再加上注解@scope(“prototype”)那麼被spring管理的類就是多例的。稍微麻煩點的是,要想擷取多例必須通過spring上下文擷取。如果直接注入,那麼注入的user還是隻是那一個。相信各位一定都看過@scope(“prototype”)這種寫法吧。但spring其實提供了常量,@scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE),常量可以防止寫錯那長串單詞。有了多例,我們有血有肉的User對象寫起來就友善了許多。

為什麼我們要這麼做呢?“把權力關進籠子”,這句話在程式設計界就是強類型、常量、枚舉這些來展現的。同樣,DDD也是把權力鎖進領域對象。避免随心所欲的service、随心所欲的repository導緻代碼後期維護成為爬“屎山”。

spring支援的模式:

1.ConfigurableBeanFactory.SCOPE_SINGLETON——單例

2.ConfigurableBeanFactory.SCOPE_PROTOTYPE——多例

擴充模式:

3.WebApplicationContext.SCOPE_APPLICATION

4.WebApplicationContext.SCOPE_GLOBAL_SESSION

5.WebApplicationContext.SCOPE_SESSION

6.WebApplicationContext.SOCPE_REQUEST

以上擴充模式看名字都能明白不做多介紹了。