Spring?Bean的作用域具體實現(單例、多例、請求、會話、Application)
概念
在 Spring Ioc&DI 戒斷,我們學習了 Spring 是如何幫助我們管理對象的
- 通過
@Controller,@Service,@Repository,@Component,@Configuration,@Bean來聲明Bean對象 - 通過
ApplicationContext或者BeanFactory來獲取對象 - 通過
@Autowired,Setter方法或者構造方法等來為應用程序注入所依賴的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);
}
}觀察運行結果

發(fā)現輸出的 bean 對象地址是一樣的,說明每次從 Spring 容器中取出來的對象都是同一個
- 這也是“單例模式”
- 單例模式:確保一個類只有一個實例,多次創(chuàng)建也不會創(chuàng)建出多個實例
Bean 的作用域是值 Bean 在 Spring 框架中的某種行為模式
比如單例作用域:表示 Bean 在整個 Spring 中只有一份,它是全局共享的。那么當其他人修改了這個值之后,那么另一個人讀到的就是被修改后的值
修改上述代碼,給 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());
}
}觀察運行結果:

dog1和dog2為同一個對象,dog2拿到了dog1設置的值
那能不能將 bean 對象設置為非單例的(每次獲取的 bean 都是一個新對象呢)
- 這就是
Bean的不同作用域了
Bean 的作用域
在 Spring 中支持 6 中作用域,后 4 種在 Spring MVC 環(huán)境才生效
singleton:單例作用域- 每個
Spring Ioc容器內同名稱的Bean只有一個實例(單例)(默認)
- 每個
prototype:原型作用域(多例作用域)- 每次使用該
bean時會創(chuàng)建新的實例(非單例)
- 每次使用該
request:請求作用域- 每個
HTTP請求生命周期內,創(chuàng)建新的實例(Web環(huán)境中,了解)
- 每個
session:會話作用域- 每個
HTTP Session生命周期內,創(chuàng)建新的實例(Web環(huán)境中,了解)
- 每個
Application:全局作用域- 每個
ServletContext生命周期里內,創(chuàng)建新的實例(web環(huán)境中,了解)
- 每個
websocket:HTTP WebSocket作用域- 每個
WebSocket生命周期內,創(chuàng)建新的實例(web環(huá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 設置代理 .proxyMode = ScopedProxyMode.TARGET_CLASS 表?這個 Bean 基于 CGLIB 實現動態(tài)代理,Request,session 和 application 作?域的 Bean 需要設置 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 容器啟動時,就已經注入了,所以多次請求訪問也不會發(fā)生變化)

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

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

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

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

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

