解讀Spring?Bean的作用域
在Spring中,bean作用域用于確定哪種類型的bean實例應(yīng)該從Spring容器中返回給調(diào)用者。
目前Spring Bean的作用域或者說范圍主要有五種
| 作用域 | 描述 |
| singleton | 在spring IoC容器僅存在一個Bean實例,Bean以單例方式存在,bean作用域范圍的默認(rèn)值。 |
| prototype | 每次從容器中調(diào)用Bean時,都返回一個新的實例,即每次調(diào)用getBean()時,相當(dāng)于執(zhí)行newXxxBean()。 |
| request | 每次HTTP請求都會創(chuàng)建一個新的Bean,該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
| session | 同一個HTTP Session共享一個Bean,不同Session使用不同的Bean。該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
| application | 限定一個Bean的作用域為ServletContext的生命周期。該作用域僅適用于web的Spring WebApplicationContext環(huán)境。 |
(1)被聲明為singleton的bean
如果bean的作用域的屬性被聲明為singleton,那么Spring Ioc容器只會創(chuàng)建一個共享的bean實例。對于所有的bean請求,只要id與該bean定義的相匹配,那么Spring在每次需要時都返回同一個bean實例。
Singleton是單例類型,就是在創(chuàng)建起容器時就同時自動創(chuàng)建了一個bean的對象,不管你是否使用,他都存在了,每次獲取到的對象都是同一個對象。注意,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)建一個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)建兩個SingletonBean對象,第二個對象獲取SingletonBean對象中的消息值得時候即使是由一個新的getBean()方法來獲取,也可以不用設(shè)置對象中消息的值就可以直接獲取SingletonBean中的消息,因為這時的消息已經(jīng)由第一個對象初始化了。
在單例中,每個Spring IoC容器只有一個實例,無論創(chuàng)建多少個對象,調(diào)用多少次getMessafe( )方法獲取它,它總是返回同一個實例。
(2)被聲明為prototype的bean
當(dāng)一個bean的作用域為prototype,表示一個bean定義對應(yīng)多個對象實例。
聲明為prototype作用域的bean會導(dǎo)致在每次對該bean請求(將其注入到另一個bean中,或者以程序的方式調(diào)用容器的getBean()方法)時都會創(chuàng)建一個新的bean實例。
prototype是原型類型,它在我們創(chuàng)建容器的時候并沒有實例化,而是當(dāng)我們獲取bean的時候才會去創(chuàng)建一個對象,而且我們每次獲取到的對象都不是同一個對象。
根據(jù)經(jīng)驗,對有狀態(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就獲取不到了,說明這兩個對象現(xiàn)在返回的就不是同一個對象實例。
(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這是一個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這三個作用域都是基于web的Spring WebApplicationContext實現(xiàn)的,只有在web環(huán)境下(比如XmlWebApplicationContext)中才能使用。 如果開發(fā)者僅僅在常規(guī)的Spring IoC容器中比如ClassPathXmlApplicationContext在中使用這些作用域,那么將會拋出一個IllegalStateException來說明使用了未知的作用域。
也就是當(dāng)用戶使用Spring的WebApplicationContext時,除了使用常規(guī)的singleton和prototype作用域之外,還可以使用另外3種Bean的作用域,即request,session和application。
在使用Web應(yīng)用環(huán)境相關(guān)的Bean作用域時,必須在Web容器中進(jìn)行一些額外的配置:
1.如果開發(fā)者使用了Spring Web MVC框架的話,每一個請求都會通過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。可以在web.xml中增加如下的Listener聲明:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>ServletContextListener只負(fù)責(zé)監(jiān)聽web容器啟動和關(guān)閉的事件,而RequestContextListener實現(xiàn)了ServletRequestListener監(jiān)聽器接口,該監(jiān)聽器監(jiān)聽http請求事件。Web服務(wù)器接收到的每一次請求都會通知該監(jiān)聽器。
而在Servlet 3.0以后,這些都能夠通過WebApplicationInitializer接口來實現(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來處理每個HTTP請求的時候都會創(chuàng)建一個新的LoginAction實例。也就是說,loginActionBean的作用域是HTTP Request級別的。
當(dāng)http請求調(diào)用作用域為request的bean的時候,每增加一個HTTP請求,Spring就會創(chuàng)建一個新的bean,在請求處理完成之后便及時銷毀這個bean。
開發(fā)者可以隨意改變實例的狀態(tài),因為通過loginAction請求來創(chuàng)建的其他實例根本看不到開發(fā)者改變的實例狀態(tài),所有創(chuàng)建的Bean實例都是根據(jù)獨立的請求來的。
(5)會話作用域
會話作用域參考如下的Bean定義
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
Spring容器會在每次調(diào)用到userPreferences時,在一個單獨的HTTP會話周期來創(chuàng)建一個新的UserPreferences實例。換言之,userPreferencesBean的作用域是HTTP Session級別的。
Session中所有http請求共享同一個請求的bean實例。Session結(jié)束后就銷毀bean。 在request-scoped作用域的Bean上,開發(fā)者可以隨意的更改實例的狀態(tài)。同樣,使用從同一個userPreferences bean定義創(chuàng)建的其他HTTP Session實例在看不到不是自己的內(nèi)部狀態(tài)的修改,因為他們是單個的HTTP會話。
每個Session請求都會創(chuàng)建新的userPreferences實例,所以開發(fā)者更改一個Bean的狀態(tài),對于其他的Bean仍然是不可見的。
(6)全局作用域
全局作用域參考如下的Bean定義
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring容器會在整個web應(yīng)用范圍使用到appPreferences的時候創(chuàng)建一個新的AppPreferences的實例。也就是說,appPreferencesBean是在ServletContext級別的,作為常規(guī)的ServletContext屬性。這種作用域在一些程度上來說和Spring的單例作用域相似,但是也有如下不同之處:
1.application作用域是每個ServletContext中包含一個,而不是每個SpringApplicationContext之中包含一個(某些應(yīng)用中可能包含不止一個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.首先需要實現(xiàn)自定義Scope類。
首先要先實現(xiàn)org.springframework.beans.factory.config.Scope這個接口,要將自定義scope集成到Spring容器當(dāng)中就必須要實現(xiàn)這個接口。接口中有兩個常用的方法,分別用于底層存儲機(jī)制獲取和刪除這個對象。
2.在實現(xiàn)一個或多個自定義Scope并測試通過之后,接下來便是如何讓Spring容器來識別新的作用域。registerScope方法就是在Spring容器中用來注冊新的作用域。
void registerScope(String scopeName, Scope scope);
其中:第一個參數(shù)是與作用域相關(guān)的全局唯一的名稱,第二個參數(shù)是準(zhǔn)備實現(xiàn)的作用域的實例,就是實現(xiàn)Scope接口的實例。
比如實現(xiàn)Scope接口的類為SimpleThreadScope,要實現(xiàn)的自定義的bean的作用域的名稱為“thread”,那就可以這么寫。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);3.在實現(xiàn)和注冊自定義的scope類之后,就可以通過如下類似的Bean定義來使用自定義的Scope:
<bean id="..." class="..." scope="thread">
另外,在自定義的Scope中,開發(fā)者也不限于僅僅通過編程方式來實現(xiàn)自定義的bean的作用域,也可以在Spring的配置文件中配置和使用自定義作用域和,比如配置CustomScopeConfigurer實例實現(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作用域的一些基本信息。僅為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springmvc實現(xiàn)自定義類型轉(zhuǎn)換器示例
本篇文章主要介紹了springmvc實現(xiàn)自定義類型轉(zhuǎn)換器示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02
Spring Boot使用Servlet及Filter過程詳解
這篇文章主要介紹了Spring Boot使用Servlet及Filter過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-07-07
java啟動jar包設(shè)置啟動參數(shù)的實現(xiàn)
本文主要介紹了java啟動jar包設(shè)置啟動參數(shù)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
java編程SpringSecurity入門原理及應(yīng)用簡介
Spring 是非常流行和成功的 Java 應(yīng)用開發(fā)框架,Spring Security 正是 Spring 家族中的成員。Spring Security 基于 Spring 框架,提供了一套 Web 應(yīng)用安全性的完整解決方案2021-09-09

