解讀Spring?Bean的作用域
在Spring中,bean作用域用于確定哪種類型的bean實(shí)例應(yīng)該從Spring容器中返回給調(diào)用者。
目前Spring Bean的作用域或者說范圍主要有五種
作用域 | 描述 |
singleton | 在spring IoC容器僅存在一個(gè)Bean實(shí)例,Bean以單例方式存在,bean作用域范圍的默認(rèn)值。 |
prototype | 每次從容器中調(diào)用Bean時(shí),都返回一個(gè)新的實(shí)例,即每次調(diào)用getBean()時(shí),相當(dāng)于執(zhí)行newXxxBean()。 |
request | 每次HTTP請求都會創(chuàng)建一個(gè)新的Bean,該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
session | 同一個(gè)HTTP Session共享一個(gè)Bean,不同Session使用不同的Bean。該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
application | 限定一個(gè)Bean的作用域?yàn)镾ervletContext的生命周期。該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
(1)被聲明為singleton的bean
如果bean的作用域的屬性被聲明為singleton,那么Spring Ioc容器只會創(chuàng)建一個(gè)共享的bean實(shí)例。對于所有的bean請求,只要id與該bean定義的相匹配,那么Spring在每次需要時(shí)都返回同一個(gè)bean實(shí)例。
Singleton是單例類型,就是在創(chuàng)建起容器時(shí)就同時(shí)自動(dòng)創(chuàng)建了一個(gè)bean的對象,不管你是否使用,他都存在了,每次獲取到的對象都是同一個(gè)對象。注意,singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中設(shè)置作用域的屬性為 singleton,如下所示:
<!-- A bean definition with singleton scope --> <bean id="..." class="..." scope="singleton"> ? ? <!-- collaborators and configuration for this bean go here --> </bean>
單例的例子
1.首先創(chuàng)建一個(gè)bean。
package com.spring.demo; public class ?SingletonBean{ ? ?private String message; ? ?public void setMessage(String message){ ? ? ? this.message ?= message; ? ?} ? ?public void getMessage(){ ? ? ? System.out.println("Your Message : " + message); ? ?} }
2.在Spring的配置文件中配置該bean。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" ? ? ? ?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ? ? ? ?xsi:schemaLocation=" ? ? ? ? http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> ? ? ? <bean id="SingletonBean" class="com.spring.demo.SingletonBean" scope="singleton"></bean> ? ? <!-- 或者 --> ? ? <!-- ?<bean id="SingletonBean" class="com.spring.demo.SingletonBean" ></bean> --> </beans>
測試該Bean是否為單例的。
package com.spring.demo; ? import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.junit.Test; ? public class TestBean { ? ? ? @Test ? ? public void textUser() ? ? { ? ? ? ? //1.獲取spring文件 ? ? ? ? ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml"); ? ? ? ? //2.由配置文件返回對象 ? ? ? ? SingletonBean singletonBeanA = (SingletonBean)context.getBean("SingletonBean"); ? ? ? ? singletonBeanA.setMessage("I'm object A"); ? ? ? ? singletonBeanA.getMessage(); ? ? ? ? SingletonBean singletonBeanB = (SingletonBean)context.getBean("SingletonBean"); ? ? ? ? singletonBeanB.getMessage(); ? ? } }
運(yùn)行結(jié)果:
由于SingletonBean是單例的作用域,創(chuàng)建兩個(gè)SingletonBean對象,第二個(gè)對象獲取SingletonBean對象中的消息值得時(shí)候即使是由一個(gè)新的getBean()方法來獲取,也可以不用設(shè)置對象中消息的值就可以直接獲取SingletonBean中的消息,因?yàn)檫@時(shí)的消息已經(jīng)由第一個(gè)對象初始化了。
在單例中,每個(gè)Spring IoC容器只有一個(gè)實(shí)例,無論創(chuàng)建多少個(gè)對象,調(diào)用多少次getMessafe( )方法獲取它,它總是返回同一個(gè)實(shí)例。
(2)被聲明為prototype的bean
當(dāng)一個(gè)bean的作用域?yàn)閜rototype,表示一個(gè)bean定義對應(yīng)多個(gè)對象實(shí)例。
聲明為prototype作用域的bean會導(dǎo)致在每次對該bean請求(將其注入到另一個(gè)bean中,或者以程序的方式調(diào)用容器的getBean()方法)時(shí)都會創(chuàng)建一個(gè)新的bean實(shí)例。
prototype是原型類型,它在我們創(chuàng)建容器的時(shí)候并沒有實(shí)例化,而是當(dāng)我們獲取bean的時(shí)候才會去創(chuàng)建一個(gè)對象,而且我們每次獲取到的對象都不是同一個(gè)對象。
根據(jù)經(jīng)驗(yàn),對有狀態(tài)的bean應(yīng)該使用prototype作用域,而對無狀態(tài)的bean則應(yīng)該使用singleton作用域。
prototype的例子。
還是上面的代碼。其他代碼不變,把Bean.xml文件中bean的作用域由singleton改為prototype。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="SingletonBean" class="com.spring.demo.SingletonBean" scope="prototype"></bean> </beans>
執(zhí)行代碼,程序的執(zhí)行結(jié)果為:
從圖上可以看出在SingletonBeanA中設(shè)置的參數(shù)值在SingletonBeanB就獲取不到了,說明這兩個(gè)對象現(xiàn)在返回的就不是同一個(gè)對象實(shí)例。
(3)使用注解定義 bean 的作用域
除了在Bean.xml文件中定義bean的作用域之外,還可以使用注解來定義 bean 的作用域。
1.在Bean中加上注解。
package com.spring.demo; import org.springframework.context.annotation.Scope;; import org.springframework.stereotype.Component; @Component("SingletonBean") @Scope("prototype") public class SingletonBean { private String message; public void setMessage(String message){ this.message = message; } public void getMessage(){ System.out.println("Your Message : " + message); } }
@Component("SingletonBean")
注解是告訴Spring這是一個(gè)bean。@Scope("prototype")
注解是告訴Spring該bean的作用域是prototype。
2.bean.xml文件修改一下。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:component-scan base-package="com.spring.demo" /> </beans>
<context:component-scan base-package="com.spring.demo" />就是掃描com.spring.demo包中的所有類的注解。
測試代碼不用變,運(yùn)行測試。
和在bean.xml中直接定義bean和其作用域是一樣的效果。其他作用域也可以使用注解方式聲明bean的作用域。
request,session和application這三個(gè)作用域都是基于web的Spring WebApplicationContext實(shí)現(xiàn)的,只有在web環(huán)境下(比如XmlWebApplicationContext)中才能使用。 如果開發(fā)者僅僅在常規(guī)的Spring IoC容器中比如ClassPathXmlApplicationContext在中使用這些作用域,那么將會拋出一個(gè)IllegalStateException來說明使用了未知的作用域。
也就是當(dāng)用戶使用Spring的WebApplicationContext時(shí),除了使用常規(guī)的singleton和prototype作用域之外,還可以使用另外3種Bean的作用域,即request,session和application。
在使用Web應(yīng)用環(huán)境相關(guān)的Bean作用域時(shí),必須在Web容器中進(jìn)行一些額外的配置:
1.如果開發(fā)者使用了Spring Web MVC框架的話,每一個(gè)請求都會通過Spring的DispatcherServlet來處理,也就沒有其他特殊的初始化配置,就不需要配置了。DispatcherServlet已經(jīng)包含了相關(guān)的狀態(tài)。
2.如果開發(fā)者使用的是低版本W(wǎng)eb容器比如Servlet 2.5的web容器,請求不是通過Spring的DispatcherServlet(比如JSF或者Struts)來處理的。那么開發(fā)者需要注冊org.springframework.web.context.request.RequestContextListener或者ServletRequestListener??梢栽趙eb.xml中增加如下的Listener聲明:
<web-app> ... <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> ... </web-app>
ServletContextListener只負(fù)責(zé)監(jiān)聽web容器啟動(dòng)和關(guān)閉的事件,而RequestContextListener實(shí)現(xiàn)了ServletRequestListener監(jiān)聽器接口,該監(jiān)聽器監(jiān)聽http請求事件。Web服務(wù)器接收到的每一次請求都會通知該監(jiān)聽器。
而在Servlet 3.0以后,這些都能夠通過WebApplicationInitializer接口來實(shí)現(xiàn)配置。
3.如果不使用Listener,也可以考慮使用Spring的RequestContextFilter,通過http過濾器進(jìn)行配置,在url-pattern中對所有的頁面進(jìn)行過濾。也是在web.xml中進(jìn)行配置。
<web-app> ... <filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ... </web-app>
配置完這些額外的配置之后,就可以使用另外的3種bean的作用域了。
(4)請求作用域
請求作用域參考如下的Bean定義
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
Spring容器會在每次用到loginAction來處理每個(gè)HTTP請求的時(shí)候都會創(chuàng)建一個(gè)新的LoginAction實(shí)例。也就是說,loginActionBean的作用域是HTTP Request級別的。
當(dāng)http請求調(diào)用作用域?yàn)閞equest的bean的時(shí)候,每增加一個(gè)HTTP請求,Spring就會創(chuàng)建一個(gè)新的bean,在請求處理完成之后便及時(shí)銷毀這個(gè)bean。
開發(fā)者可以隨意改變實(shí)例的狀態(tài),因?yàn)橥ㄟ^loginAction請求來創(chuàng)建的其他實(shí)例根本看不到開發(fā)者改變的實(shí)例狀態(tài),所有創(chuàng)建的Bean實(shí)例都是根據(jù)獨(dú)立的請求來的。
(5)會話作用域
會話作用域參考如下的Bean定義
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
Spring容器會在每次調(diào)用到userPreferences時(shí),在一個(gè)單獨(dú)的HTTP會話周期來創(chuàng)建一個(gè)新的UserPreferences實(shí)例。換言之,userPreferencesBean的作用域是HTTP Session級別的。
Session中所有http請求共享同一個(gè)請求的bean實(shí)例。Session結(jié)束后就銷毀bean。 在request-scoped作用域的Bean上,開發(fā)者可以隨意的更改實(shí)例的狀態(tài)。同樣,使用從同一個(gè)userPreferences bean定義創(chuàng)建的其他HTTP Session實(shí)例在看不到不是自己的內(nèi)部狀態(tài)的修改,因?yàn)樗麄兪菃蝹€(gè)的HTTP會話。
每個(gè)Session請求都會創(chuàng)建新的userPreferences實(shí)例,所以開發(fā)者更改一個(gè)Bean的狀態(tài),對于其他的Bean仍然是不可見的。
(6)全局作用域
全局作用域參考如下的Bean定義
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring容器會在整個(gè)web應(yīng)用范圍使用到appPreferences的時(shí)候創(chuàng)建一個(gè)新的AppPreferences的實(shí)例。也就是說,appPreferencesBean是在ServletContext級別的,作為常規(guī)的ServletContext屬性。這種作用域在一些程度上來說和Spring的單例作用域相似,但是也有如下不同之處:
1.application作用域是每個(gè)ServletContext中包含一個(gè),而不是每個(gè)SpringApplicationContext之中包含一個(gè)(某些應(yīng)用中可能包含不止一個(gè)ApplicationContext)。
2.application作用域僅僅作為ServletContext的屬性可見,單例Bean是ApplicationContext可見。
接下來再來簡單的學(xué)習(xí)下在Spring當(dāng)中如何自定義作用域:
在Spring 2.0中,Spring的Bean作用域機(jī)制是可以擴(kuò)展的,這意味著,你不僅可以使用Spring提供的預(yù)定義Bean作用域,還可以定義自己的作用域,甚至重新定義現(xiàn)有的作用域(不提倡這么做,而且你不能覆蓋內(nèi)置的singleton和prototype作用域)
(7)自定義作用域
除了使用Spring已經(jīng)定義好的作用域之外,還可以自定義bean的作用域。
要底線自定義作用域
1.首先需要實(shí)現(xiàn)自定義Scope類。
首先要先實(shí)現(xiàn)org.springframework.beans.factory.config.Scope這個(gè)接口,要將自定義scope集成到Spring容器當(dāng)中就必須要實(shí)現(xiàn)這個(gè)接口。接口中有兩個(gè)常用的方法,分別用于底層存儲機(jī)制獲取和刪除這個(gè)對象。
2.在實(shí)現(xiàn)一個(gè)或多個(gè)自定義Scope并測試通過之后,接下來便是如何讓Spring容器來識別新的作用域。registerScope方法就是在Spring容器中用來注冊新的作用域。
void registerScope(String scopeName, Scope scope);
其中:第一個(gè)參數(shù)是與作用域相關(guān)的全局唯一的名稱,第二個(gè)參數(shù)是準(zhǔn)備實(shí)現(xiàn)的作用域的實(shí)例,就是實(shí)現(xiàn)Scope接口的實(shí)例。
比如實(shí)現(xiàn)Scope接口的類為SimpleThreadScope,要實(shí)現(xiàn)的自定義的bean的作用域的名稱為“thread”,那就可以這么寫。
Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope);
3.在實(shí)現(xiàn)和注冊自定義的scope類之后,就可以通過如下類似的Bean定義來使用自定義的Scope:
<bean id="..." class="..." scope="thread">
另外,在自定義的Scope中,開發(fā)者也不限于僅僅通過編程方式來實(shí)現(xiàn)自定義的bean的作用域,也可以在Spring的配置文件中配置和使用自定義作用域和,比如配置CustomScopeConfigurer實(shí)例實(shí)現(xiàn)自定義的作用域,聲明作用域名稱為“thread”,就可以在xml文件中做如下類似的定義。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> <property name="scopes"> <map> <entry key="thread"> <bean class="org.springframework.context.support.SimpleThreadScope"/> </entry> </map> </property> </bean> <bean id="bar" class="x.y.Bar" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="foo" class="x.y.Foo"> <property name="bar" ref="bar"/> </bean> </beans>
以上就是Spring Bean作用域的一些基本信息。僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatis批量插入大量數(shù)據(jù)(1w以上)
MyBatis進(jìn)行批量插入數(shù)時(shí),一次性插入超過一千條的時(shí)候MyBatis開始報(bào)錯(cuò),本文主要介紹了MyBatis批量插入大量數(shù)據(jù)的解決方法,感興趣的可以了解一下2022-01-01springboot yml配置文件定義list集合、數(shù)組和map以及使用中的錯(cuò)誤
這篇文章主要介紹了springboot yml配置文件定義list集合、數(shù)組和map以及使用中遇到的錯(cuò)誤問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07java token生成和校驗(yàn)的實(shí)例代碼
這篇文章主要介紹了java token生成和校驗(yàn)的實(shí)例代碼,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09springboot+EHcache 實(shí)現(xiàn)文章瀏覽量的緩存和超時(shí)更新
這篇文章主要介紹了springboot+EHcache 實(shí)現(xiàn)文章瀏覽量的緩存和超時(shí)更新,問題描述和解決思路給大家介紹的非常詳細(xì),需要的朋友可以參考下2017-04-04Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之漢諾塔
漢諾塔是源于印度一個(gè)古老傳說的益智玩具。大梵天創(chuàng)造世界時(shí)做了三根石柱,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規(guī)定,在小圓盤上不能放大圓盤,三根柱子之間一次只能移動(dòng)一個(gè)圓盤2022-02-02spring boot udp或者tcp接收數(shù)據(jù)的實(shí)例詳解
這篇文章主要介紹了spring boot udp或者tcp接收數(shù)據(jù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12