Spring Bean的作用域有singleton、prototype、request、session、application、websocket。
singleton
singleton就是常見的單例模式的一種實現,默認情況下bean的作用域就是singleton,所以無需做任何配置。
需要注意的是這里所指的單例并不是應用程序中的單例,而是在當前bean容器中是單例的。當bean被定義為單例時,spring容器中會共享這個bean實例。
prototype
當bean的scope設定為prototype時,如果這個bean需要被引用(注入到某個bean中或者被getBean()方式查找),spring容器都會根據當前bean定義創建一個全新的bean實例對象返回。
實現方式
- 通過xml配置
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
- 通過@Scope
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- 通過BeanDefinitionBuilder#setScope
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition (User.class);
builder.addPropertyValue ("name", "bd-user")
.setScope (BeanDefinition.SCOPE_PROTOTYPE);
request & session & application
這幾個作用域只能web環境中有效,如果是在非web的spring環境使用這些作用域,容器會拋出IllegalStateException。
request
被定義成request作用域,即每次請求都會創建一個新的bean實例。
session
被定義成session作用域,即在同一個session環境中共享bean實例
application
被定義成application作用域,即在servlet上下文環境中共享bean實例
singleton類型bean注入prototype類型bean問題
因為bean的依賴關系是在實例化時解析完成的,所以singleton類型bean中的注入prototype類型bean的過程也是只有一次,那么其實每次使用的都是同一個prototype類型bean對象。
如果想singleton類型bean在運行時重新獲取新的prototype類型bean實例,spring提供了method injection的方式解決這個問題 【方法注入非這次的學習重點,暫不過多論述】。
源碼解析
源碼入口,作用域判斷的主要邏輯在AbstractBeanFactory的doGetBean:
//調用棧
AbstractApplicationContext#refresh
->AbstractApplicationContext#finishBeanFactoryInitialization
->AbstractApplicationContext#preInstantiateSingletons
->DefaultListableBeanFactory#preInstantiateSingletons
->AbstractBeanFactory#getBean
->AbstractBeanFactory#doGetBean
-
下圖是作用域為Singleton的處理入口
singleton作用域處理邏輯.png -
下圖是作用域為Prototype的處理入口
prototype作用域處理邏輯.png
-
下圖是其他作用域的處理入口,如request、session、application、websocket、自定義作用域
其他作用域處理邏輯.png
request&session的處理入口
-
request和session的處理入口是AbstractRequestAttributesScope#get
request和session的處理邏輯
application的處理入口
-
application的處理入口時ServletContextScope#get
image.png