spring之Bean的作用域--singleton & prototype


  首先,我們要理解什么叫Bean的作用域。我們都知道變量的作用域,即變量起作用的區域。類比可知,spring的Bean的作用域就是實例起作用的區域。

  spring的Bean的作用域包括單例(singleton)、原型(prototype)、request、session。

  singleton 被標注為singleton的類,只會被實例化一次,這個實例可以無限重復注入。

  prototype 被標注為prototype的類可以被實例化多次,每個實例只能注入一次,即每次注入prototype的實例時,要檢查這個實例是否已經在其他地方注入過,如果已經注入過,則不能再使用這個實例,要新建一個實例。

  request,是一個請求周期內,實例可以重復注入。超過一個請求周期,再要注入就要新建實例,原來的實例不能使用了。

  session,是一個會話周期內,實例可以重復注入。超過一個會話周期,再要注入就要新建實例,原來的實例不能使用了。

  默認情況下,spring的Bean默認的作用域為單例,即在整個應用的周期內,每個類只創建一個實例。在大多數情況下,這種作用域是可以滿足要求的。但是對於像電商網站的購物車這樣的場景,單例作用域就不適用了,因為每個人的購物車都必須是獨立的,不能使用同一個。

  寫到這里的時候,我想起了自己做過的一個助學貸款的項目,當時我並沒有關注過spring的Bean的作用域的問題,也就是說所有的學生使用的實例都是相同的,那為什么項目沒有出現錯誤呢?畢竟每個的學生信息都是不同的,如果共享單例,必然會發生混亂。后來想通了,所有使用依賴注入的類都是service、dao,即這些實例都是單例的,那些存儲信息的對象都是在方法中new出來的,哎,雖然事情早已過去,還是讓我虛驚一場。

 

  我們先實驗一下prototype類型,將prototype類型的Bird注入到UserController3中,代碼如下:

 

package com.zaoren.bean;

import java.util.Date;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Bird extends Animal {

    @Override
    public void move() {
        Date d = new Date();
        d.getDay();
        d.getDate();
        this.play();
    }
    
    @Override
    public void play() {
        
    }
}

 

 

package com.zaoren.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zaoren.bean.Bird;

@RequestMapping("user3")
@Controller
public class UserController3 {
    
    @Autowired
    private Bird bird;

    @RequestMapping("test")
    public void test() {
        System.out.println("bird = "+bird);
    }
}

兩次請求test方法,打印結果為:

打印結果竟然不符合我們的期望。明明將Bird類標注為prototype類型了,為什么兩次請求注入的是同一個實例呢?

  這里我們要注意,spring處理請求時,首先要在容器中找到一個controller實例,用這個controller實例來處理請求。上面的代碼中,類UserController3並沒有標注為prototype類型,所以它默認為singleton類型,因此兩次請求實際上是由同一個UserController3實例來處理的,同一個UserController3實例的Bird屬性自然是相同的。

  這里可以看出,我們查找問題時,要使用聯系和發散的思維方式。將局部問題放在整體過程中審視,並聯系相關的知識和過往經驗。

  由此可以看出,要想試驗prototype的情況,需要將作用域同時設置在類Bird和類UserController3上,代碼如下:

package com.zaoren.bean;

import java.util.Date;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Bird extends Animal {

    @Override
    public void move() {
        Date d = new Date();
        d.getDay();
        d.getDate();
        this.play();
    }
    
    @Override
    public void play() {
        
    }
}

 

package com.zaoren.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.zaoren.bean.Bird;

@RequestMapping("user3")
@Controller
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserController3 {
    
    @Autowired
    private Bird bird;
    
    @RequestMapping("test")
    public void test() {
        System.out.println("bird = "+bird);
    }
}

 

請求test方法,控制台輸出結果為:

這一次,在類UserController3上加了prototype作用域,所以兩次請求調用的controller是不同的實例,兩個UserController3實例都需要被注入一個Bird實例,由於類Bird的作用域為prototype,所以注入兩個UserController3實例的Bird實例是不同的,即第二次請求創建UserController3實例時,又重新創建了一個Bird實例來注入,沒有注入原來的Bird實例,控制台的輸出結果符合我們的期望。

 

  這里,我又想到了另一個問題。我們的代碼中,並沒有將controller注入到任何地方,那為什么還可以對controller使用作用域呢?

  要知道,我們最初學習的時候,http請求都是由我們自己編寫的servlet處理的,而spring是一個框架,它內部實際上封裝了servlet,只不過servlet這個類並沒有展示給我看。我們發起請求時,請求實際上先到達那個我們看不到的servlet,而servlet實例已經被注入了我們的controller實例,具體的業務處理正是由我們的controller實例來處理的。所以說controller的作用域也會起作用。


免責聲明!

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



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