spring單例如何改多例

spring單例改多例

單例:就像你一生隻有一個老婆。也就是對象始終是同一個。

多例:就像你一生有好多個老婆。也就是對象每次都是新的。

spring默認是單例模式(就每個請求都是用的同一對象,對於dao層肯定是棒棒的),但是有的時候,我們需要每個請求都

產生一個新的對象,就如做微信小程序,用scoket、不可能一直都用一個來接收的,因為需要分配房間,所以需要使用到多例。

對於struts2來說,action必須用多例,因為action本身含有請求參數的值,即可改變的狀態;

而對於STRUTS1來說,action則可用單例,因為請求參數的值是放在actionForm中,而非action中的;

配置某個類為多例:

<bean id="user" class="modle.User" scope="prototype">  </bean>

但是比如service配置的是多例、dao又是單例,那肯定不行瞭,因為不能自動註入。

所以需要獲得一個新的dao實例,但是手動new的都不可以,那麼就借助通過實現 BeanFactoryAware 接口來獲得factory

附加自己待測試

public class userService implements BeanFactoryAware{   
  private UserDao userDao;
  private BeanFactory factory;
  public void userService (){
    this.userDao= (UserDao)factory.getBean("userDao");
    userDao.work();
  }
  public UserDao getUserDao() {
        return userDao;
  }
public void setBeanFactory(BeanFactory f) throws BeansException {
    factory = f;
  }
 
}

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

以上擴展模式看名字都能明白不做多介紹瞭。希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: