Spring?Bean的作用域具體實(shí)現(xiàn)(單例、多例、請求、會話、Application)
概念
在 Spring Ioc&DI 戒斷,我們學(xué)習(xí)了 Spring 是如何幫助我們管理對象的
- 通過
@Controller,@Service,@Repository,@Component,@Configuration,@Bean來聲明Bean對象 - 通過
ApplicationContext或者BeanFactory來獲取對象 - 通過
@Autowired,Setter方法或者構(gòu)造方法等來為應(yīng)用程序注入所依賴的Bean對象
我們來簡單回顧一下
- 通過
@Bean聲明bean,把bean存在Spring容器中
public class Dog {
private String name;
public void setName(String name) {
this.name = name;
}
}@Component
public class DogBeanConfig {
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("旺旺");
return dog;
}
}- 從
Spring容器中獲取Bean- 通過在代碼中直接注入
ApplicationContext的方式
- 通過在代碼中直接注入
@SpringBootTest
class ApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads() {
DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);;
System.out.println(dog1);
}
}司改代碼,從 Spring 容器中多次獲取 Bean
@SpringBootTest
class ApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads() {
DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);;
System.out.println(dog1);
DogBeanConfig dog2 = applicationContext.getBean(DogBeanConfig.class);;
System.out.println(dog2);
}
}觀察運(yùn)行結(jié)果

發(fā)現(xiàn)輸出的 bean 對象地址是一樣的,說明每次從 Spring 容器中取出來的對象都是同一個
- 這也是“單例模式”
- 單例模式:確保一個類只有一個實(shí)例,多次創(chuàng)建也不會創(chuàng)建出多個實(shí)例
Bean 的作用域是值 Bean 在 Spring 框架中的某種行為模式
比如單例作用域:表示 Bean 在整個 Spring 中只有一份,它是全局共享的。那么當(dāng)其他人修改了這個值之后,那么另一個人讀到的就是被修改后的值
修改上述代碼,給 UserController 添加屬性 name
@SpringBootTest
class ApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads() {
Dog dog1 = applicationContext.getBean(Dog.class);
dog1.setName("狗狗1");
System.out.println(dog1);
System.out.println(dog1.getName());
Dog dog2 = applicationContext.getBean(Dog.class);;
System.out.println(dog2);
System.out.println(dog2.getName());
}
}觀察運(yùn)行結(jié)果:

dog1和dog2為同一個對象,dog2拿到了dog1設(shè)置的值
那能不能將 bean 對象設(shè)置為非單例的(每次獲取的 bean 都是一個新對象呢)
- 這就是
Bean的不同作用域了
Bean 的作用域
在 Spring 中支持 6 中作用域,后 4 種在 Spring MVC 環(huán)境才生效
singleton:單例作用域- 每個
Spring Ioc容器內(nèi)同名稱的Bean只有一個實(shí)例(單例)(默認(rèn))
- 每個
prototype:原型作用域(多例作用域)- 每次使用該
bean時會創(chuàng)建新的實(shí)例(非單例)
- 每次使用該
request:請求作用域- 每個
HTTP請求生命周期內(nèi),創(chuàng)建新的實(shí)例(Web環(huán)境中,了解)
- 每個
session:會話作用域- 每個
HTTP Session生命周期內(nèi),創(chuàng)建新的實(shí)例(Web環(huán)境中,了解)
- 每個
Application:全局作用域- 每個
ServletContext生命周期里內(nèi),創(chuàng)建新的實(shí)例(web環(huán)境中,了解)
- 每個
websocket:HTTP WebSocket作用域- 每個
WebSocket生命周期內(nèi),創(chuàng)建新的實(shí)例(web環(huán)境中,了解)
- 每個
我們來看簡單的代碼實(shí)現(xiàn)
定義幾個不同作用域的 bean
@Component
public class DogBeanConfig {
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("旺旺");
return dog;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singleDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototype() {
Dog dog = new Dog();
return dog;
}
@Bean
@RequestScope
public Dog requestDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@SessionScope
public Dog sessionDog() {
Dog dog = new Dog();
return dog;
}
@Bean
@ApplicationScope
public Dog applicationDog() {
Dog dog = new Dog();
return dog;
}
}@RequestScope等同于@Scope (value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)@SessionScope等同于@Scope (value =WebApplicationContext.SCOPE_SESSION, proxyMode =ScopedProxyMode.TARGET_CLASS)@ApplicationScope等同于@Scope (value =WebApplicationContext. SCOPE_APPLICATION, proxyMode =ScopedProxyMode. TARGET_CLASS)
proxyMode ?來為 springbean 設(shè)置代理 .proxyMode = ScopedProxyMode.TARGET_CLASS 表?這個 Bean 基于 CGLIB 實(shí)現(xiàn)動態(tài)代理,Request,session 和 application 作?域的 Bean 需要設(shè)置 proxyMode
代碼測試
測試不同作用域的 bean 取到的對象是否一樣
@RestController
public class DogController {
@Autowired
private Dog singleDog;
@Autowired
private Dog prototypeDog;
@Autowired
private Dog requestDog;
@Autowired
private Dog sessionDog;
@Autowired
private Dog applicationDog;
@Autowired
private ApplicationContext applicationContext;
@RequestMapping("/single")
public String single(){
Dog contexDog = (Dog) applicationContext.getBean("singleDog");
return "dog: " + singleDog.toString() + ", contextDog: " + contexDog;
}
@RequestMapping("/prototype")
public String prototype() {
Dog contextDog = (Dog) applicationContext.getBean("prototypeDog");
return "dog: " + prototypeDog.toString() + ", contextDog: " + contextDog;
}
@RequestMapping("/request")
public String request(){
Dog contexDog = (Dog) applicationContext.getBean("requestDog");
return "dog: " + requestDog.toString() + ", contextDog: " + contexDog;
}
@RequestMapping("/session")
public String session() {
Dog contextDog = (Dog) applicationContext.getBean("sessionDog");
return "dog: " + sessionDog.toString() + ", contextDog: " + contextDog;
}
@RequestMapping("/application")
public String application() {
Dog contextDog = (Dog) applicationContext.getBean("applicationDog");
return "dog: " + applicationDog.toString() + ", contextDog: " + contextDog;
}
}- 每個請求都獲取兩次
Bean @Autowired和applicationContext.getBean("singleDog")都是從Spring容器中獲取對象
觀察 Bean 的作用域
1.單例作用域
地址: http://127.0.0.1:8080/single
多次訪問,得到的都是同一個對象,并且 @Autowired 和 applicationContext.getBean() 也是同一個對象

2.多例作用域
地址: http://127.0.0.1:8080/prototype
觀察 ContextDog,每次獲取的對象都不一樣(注入的對象在 Spring 容器啟動時,就已經(jīng)注入了,所以多次請求訪問也不會發(fā)生變化)

3.請求作用域
地址: http://127.0.0.1:8080/request
在一次請求中,@Autowired 和 applicationContext.getBean() 也是同一個對象,但是每次請求,都會重新創(chuàng)建對象

4.會話作用域
地址: http://127.0.0.1:8080/session
在一個 session 中,多次請求,獲取到的對象都是同一個

換一個瀏覽器訪問,發(fā)現(xiàn)會重新創(chuàng)建對象(另一個 session)

5.Application作用域
地址: http://127.0.0.1:8080/application
在一個應(yīng)用中,多次訪問都是同一個對象

Application scope就是對于整個web容器來說,bean的作用域是ServletContext級別的- 這個和
singleton有點(diǎn)類似,區(qū)別在于:Application scope是ServletContext的單例,singleton是一個ApplicationContext的單例 - 在一個
web容器中ApplicationContext可以有多個(了解即可)
到此這篇關(guān)于Spring Bean的作用域具體實(shí)現(xiàn)(單例、多例、請求、會話、Application)的文章就介紹到這了,更多相關(guān)Spring Bean作用域內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot下實(shí)現(xiàn)RedisTemplate?List?清空
我們經(jīng)常會使用Redis的List數(shù)據(jù)結(jié)構(gòu)來存儲一系列的元素,當(dāng)我們需要清空一個List時,可以使用RedisTemplate來實(shí)現(xiàn),本文就來詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下2024-01-01
示例解析java重載Overloading與覆蓋Overriding
這篇文章主要介紹了java重載Overloading與覆蓋Overriding的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
深入分析Spring BeanDefinition的構(gòu)造元信息
Bean Definition是一個包含Bean元數(shù)據(jù)的對象,它描述了如何創(chuàng)建Bean實(shí)例、Bean屬性的值以及Bean之間的依賴關(guān)系,本文將帶大家深入分析Spring BeanDefinition的構(gòu)造元信息,需要的朋友可以參考下2024-01-01
Java中的拷貝數(shù)組CopyOnWriteArrayList詳解
這篇文章主要介紹了Java中的拷貝數(shù)組CopyOnWriteArrayList詳解,ArrayList和LinkedList都不是線程安全的,如果需要線程安全的List,可以使用synchronizedList來生成一個同步list,但是這個同步list的方法都是通過synchronized修飾來保證同步的,需要的朋友可以參考下2023-12-12
Java如何根據(jù)word模板導(dǎo)出數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)根據(jù)word模板導(dǎo)出數(shù)據(jù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-05-05
SpringBoot如何利用Twilio?Verify發(fā)送驗(yàn)證碼短信
Twilio提供了一個名為?Twilio?Verify?的服務(wù),專門用于處理驗(yàn)證碼的發(fā)送和驗(yàn)證,下面我們就來看看如何使用Twilio?Verify實(shí)現(xiàn)發(fā)送驗(yàn)證碼短信吧2025-03-03

