1.5,bean 作用域
創建bean定義時,將通過一個配方來創建該bean的實際實體,把bean定義解釋稱配方這一思想跟重要,因為它意味著與類一樣,您可以從一個配方中創建許多物件實體,
bean definition
是用來描述bean的一個結構體,就像每個人體檢指標一樣,進出一趟醫院能拿到體檢單,
bean在進入Spring流程中也會拿到屬于自己的體檢單,
雖然醫院不能根據體檢單創造出人類,但是Spring能根據bean definition創造出實體
您不僅可以控制從bean definition 創建的物件中的各種依賴項和配置值,還可以控制從特定bean定義創建的物件的作用域,
這種方法功能強大且靈活,因為您可以選擇通過配置創建的物件的作用域,而不必在Java類層次上處理物件的范圍,
可以將bean定義為部署在多種作用域中的一種,
Spring框架支持6種作用域,其中4種作用域只有在使用支持web的ApplicationContext時才可用,
您還可以創建自定義作用域,
| Bean作用域范圍 | 描述 |
|---|---|
| singleton | (默認)單例物件,容器中只存在一個物件, |
| prototype | 每次使用都會實體化新的物件 |
| request | 將單個bean定義的范圍限定為單個HTTP請求的生命周期, 也就是說,每個HTTP請求都有一個bean實體, 僅在支持web的Spring ApplicationContext背景關系中有效, |
| session | 每一次HTTP請求都會產生一個新的bean, 同時該bean僅在當前HTTP session內有效, 僅在可感知網路的Spring背景關系中有效ApplicationContext, |
| application | 將單個bean定義的作用域限定為的生命周期ServletContext, 僅在可感知網路的Spring背景關系中有效ApplicationContext, |
| websocket | 將單個bean定義的作用域限定為的生命周期WebSocket, 僅在可感知網路的Spring背景關系中有效ApplicationContext, (這個scope沒在原始碼中找到,暫不清楚實作邏輯, 但應該和之前的大同小異) |
從Spring 3.0開始,執行緒作用域可用,但默認情況下未注冊,有關更多資訊,請參見的檔案 SimpleThreadScope,有關如何注冊此自定義范圍或任何其他自定義范圍的說明,請參閱 使用自定義范圍,
說一下大致流程(非檔案內容)
https://gitee.com/cclookeme/tocmat-main
貼一個git地址,這個工程演示如何用最簡單的代碼啟動一個Spring應用,非SpringBoot
拿出來大致一說
1.Tomcat啟動會查找ServletContainerInitializer實作類并執行其中的onStartup方法,
2.Spring-web模塊存在ServletContainerInitializer實作類,所以Tomcat啟動會呼叫Spring-web的代碼,
3.但是我們用Spring框架的話不需要實作這個介面,實作一個Spring的介面WebApplicationInitializer,
4.就可以由Tomcat呼叫Spring-web的ServletContainerInitializer實作類,
再由Spring-web模塊呼叫我們的WebApplicationInitializer實作類
//WebApplicationInitializer 實作方式如下(SpringMvc官網提供地址如下)
//https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web.html#spring-web
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
//ac.refresh(); 官方檔案提供了這樣一行代碼但是我們為了使用application Scope并不需要這個代碼
// Create and register the DispatcherServlet
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
有了如上代碼,我們就有了一個ApplicationContext環境,
Tomcat啟動見git專案就可以了,那個不重要,
我們就假定現在Tomcat已經啟動,啟動并執行到我們自定義的WebApplicationInitializer中了
在這個自定義方法中我們實體化了AnnotationConfigWebApplicationContext這個物件,
把這個物件帶進了DispatcherServlet,以至于ServletContext中以備后續使用,
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
ac.register(AppConfig.class);
上邊兩行代碼僅僅是指定了一個Config類,這個類上有很多注解相當于SpringBoot的啟動類
還有其他的引數需要Spring自己來進行處理,所有后續會有這個方法來豐富ApplicationContext
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
//這個方法只看這個地方 如果之前我們自定義代碼里面執行了ac.refresh()
//那么這個地方的active會是true,那就沒法進入if里面
//而if里面則是給ApplicationContext賦值了ServletContext
//最后也會呼叫refresh方法
//而ServletContext的作用就是我們的 application Scope
//如果不注入ServletContext 則就無法啟用這個 Scope
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
.
.
.
.
return wac;
}
在看一個refresh()里執行的代碼
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
@Nullable ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
//這里判斷了sc sc就是上邊我們要注入的ServletContext 沒注入的話這里就不注冊決議 application Scope的bean
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
.
.
.
}
在之后詳細Spring啟動代碼我們不用看了
我們在看下Spring獲取bean時候怎么處理的Scope
//如果是單例 直接獲取單例池中的物件 單例池中不存在執行createBean方法
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
//如果是原型直接就createBean重新生成物件
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
//其他Scope
//首先獲取當前bean的Scope
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ′" + beanName + "'");
}
//根據Scope獲取決議當前Scope的bean
//這里也就是給了我們自定義Scope的可能 我們可以自己定義Scope
//再定義一個Scope的決議類就哦了
Scope scope = this.scopes.get(scopeName);
//如果沒找到決議類則報錯 就像前邊的ServletContext沒注入一樣
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
//這里的get是可以自定已實作快取了,比如同一個request用一個實體
//相同sessionId 用一個實體
//如果沒找到對應的實體則createBean 生成
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
下邊介紹各個Scope內容和用法(官方檔案)
1.5.1, The Singleton Scope
換句話說,當您定義一個bean定義并且它的作用域是一個單例物件時,Spring IoC容器會創建該bean定義定義的物件的一個實體,這個單一實體存盤在這樣的單例bean的快取中,對這個已命名bean的所有后續請求和參考都會回傳快取的物件,
<bean id="myDao" ></bean>
<bean id="a" >
<property name="ff" ref="myDao"></property>
</bean>
<bean id="b" >
<property name="ff" ref="myDao"></property>
</bean>
<bean id="c" >
<property name="ff" ref="myDao"></property>
</bean>
a,b,c三個物件持有的ff屬性對應的都是一個實體
Spring的單例bean概念不同于“四人幫”(GoF)模式書中定義的單例模式,
GoF單例對物件的作用域進行硬編碼,這樣每個類加載器都會創建一個且只有一個特定類的實體,
Spring單例的作用域最好描述為單個容器的單個bean,
這意味著,如果您在單個Spring容器中為特定類定義了一個bean,那么Spring容器將創建由該bean定義定義的類的一個且僅一個實體,
單例作用域是Spring中的默認作用域,要在XML中定義一個單例的bean,你可以像下面的例子那樣定義一個bean:
<bean id="accountService" />
<!-- 下面的方法是等效的,但是是多余的(單例范圍是默認的) -->
<bean id="accountService" scope="singleton"/>
1.5.2,The Prototype Scope
bean部署的非單例原型范圍導致每次對特定bean發出請求時都創建一個新的bean實體,也就是說,該bean被注入到另一個bean中,或者您通過容器上的getBean()方法呼叫請求它,作為規則,您應該對所有有狀態bean使用原型作用域,對無狀態bean使用單例作用域,
<bean id="myDao" scope="prototype"></bean>
<bean id="a" scope="prototype">
<property name="ff" ref="myDao"></property>
</bean>
<bean id="b" scope="prototype">
<property name="ff" ref="myDao"></property>
</bean>
<bean id="c" scope="prototype">
<property name="ff" ref="myDao"></property>
</bean>
a,b,c持有不同的 myDao實體,前提a,b,c必須都不是單例
(資料訪問物件(DAO)通常不配置為原型,因為典型的DAO不持有任何會話狀態,)
下面的例子用XML將bean定義為原型:
<bean id="accountService" scope="prototype"/>
與其他作用域相比,Spring不管理原型bean的完整生命周期,容器實體化、配置和組裝原型物件并將其交給客戶端,而不進一步記錄該原型實體,
因此,盡管初始化生命周期回呼方法在所有物件上都被呼叫,但在原型的情況下,配置的銷毀生命周期回呼不會被呼叫,
客戶端代碼必須清理原型范圍的物件并釋放原型bean所持有的昂貴資源,
要讓Spring容器釋放原型作用域bean所持有的資源,請嘗試使用自定義BeanPostProcessor,它持有對需要清理的bean的參考,
BeanPostProcessor介面定義了回呼方法,
您可以實作這些方法來提供您自己的(或覆寫容器的默認值)實體化邏輯、依賴項決議邏輯等等,
如果您想在Spring容器完成實體化、配置和初始化bean之后實作一些自定義邏輯,
您可以插入一個或多個自定義BeanPostProcessor實作,
您可以配置多個BeanPostProcessor實體,
并且可以通過設定order屬性來控制這些BeanPostProcessor實體運行的順序,
只有當BeanPostProcessor實作了有序介面時,才能設定此屬性,
如果您撰寫自己的BeanPostProcessor,也應該考慮實作Ordered介面,
以上是官方解釋,通俗的說就是
BeanPostProcessor有一個方法可以在bean實體化之后被執行,那時候可以拿到bean的參考,并且修改bean
1.5.3,Singleton Bean 依賴 Prototype Bean
當您使用帶有原型bean依賴項的單例范圍bean時,請注意依賴項是在實體化時決議的,
因此,如果您將一個原型作用域的bean注入到一個單例作用域的bean中,那么只有一次原型bean的實體化,然后注入到單例bean中,
實體化單例bean時生成的原型bean實體是提供給單例作用域bean的唯一實體,
但是,假設您希望單例作用域bean在運行時重復獲得原型作用域bean的新實體,
您不能依賴地將原型作用域的bean注入到單例bean中,因為該注入只在Spring容器實體化單例bean并決議和注入其依賴項時發生一次,
如果您不止一次地需要原型bean在運行時的新實體,請參閱一起來讀官方檔案-----SpringIOC(04)1.4.6小節
1.5.4,request,session,application,和websocket范圍
在request,session,application,和websocket范圍只有當你使用一個基于web的Spring可ApplicationContext實作(例如XmlWebApplicationContext),
如果您將這些作用域與常規的Spring IoC容器一起使用,例如ClassPathXmlApplicationContext,則會引發抱怨未知bean作用域的IllegalStateException例外,
<bean id="serviceTwo" scope="application"/>
Caused by: java.lang.IllegalStateException: No Scope registered for scope name 'application'
初始Web配置
為了支持bean的范圍界定在request,session,application,和 websocket(即具有web作用域bean),需要在定義你的bean之前做少量的初始配置,
如何完成此初始設定取決于您的特定Servlet環境,
如果您在Spring Web MVC中訪問作用域化的bean,實際上是在Spring處理的請求中,則DispatcherServlet不需要特殊的設定, DispatcherServlet已經公開了所有相關狀態,
對于Servlet 3.0+,可以使用該WebApplicationInitializer 介面以編程方式完成此操作,
public class LearnSpringMain implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext aa =
new AnnotationConfigWebApplicationContext();
aa.register(LearnSpringConfig.class);
aa.setServletContext(servletContext);
DispatcherServlet dispatcherServlet = new DispatcherServlet(aa);
dispatcherServlet.setEnableLoggingRequestDetails(true);
ServletRegistration.Dynamic ds = servletContext.addServlet("dispatcherServlet",dispatcherServlet);
ds.addMapping("/*");
ds.setLoadOnStartup(1);
}
}
如果您使用Servlet 2.5 Web容器,并且在Spring之外處理請求 DispatcherServlet(例如,使用JSF或Struts時),則需要注冊 org.springframework.web.context.request.RequestContextListener ServletRequestListener,
或者,或者對于較舊的容器,將以下宣告添加到Web應用程式的web.xml檔案中:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
另外,如果您的監聽器設定存在問題,請考慮使用Spring的 RequestContextFilter,過濾器映射取決于周圍的Web應用程式配置,因此您必須適當地對其進行更改,以下清單顯示了Web應用程式的過濾器部分:
<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>
DispatcherServlet,RequestContextListener和RequestContextFilter都做完全相同的事情,即將HTTP請求物件系結到Thread為該請求提供服務的物件,這使得在請求鏈和會話范圍內的Bean可以在呼叫鏈的更下游使用,
Request scope
考慮以下XML配置來定義bean:
<bean id="loginAction" scope="request"/>
Spring容器通過為每個HTTP請求使用LoginAction bean定義來創建LoginAction bean的一個新實體,也就是說,loginAction bean的作用域在HTTP請求級別,您可以隨意更改所創建實體的內部狀態,因為從相同loginAction bean定義創建的其他實體不會看到這些狀態更改,它們是針對個別請求的,當請求完成處理時,作用域為該請求的bean將被丟棄,
當使用注釋驅動的組件或Java配置時,可以使用@RequestScope注釋將組件分配到請求范圍,下面的例子展示了如何做到這一點:
@RequestScope
@Component
public class LoginAction {
// ...
}
Session Scope
考慮以下XML配置來定義bean:
<bean id="userPreferences" scope="session"/>
Spring容器通過使用單個HTTP會話生存期的UserPreferences bean定義來創建一個UserPreferences bean的新實體,換句話說,userPreferences bean有效地限定了HTTP會話級別的范圍,與請求范圍內bean一樣,你可以改變內部狀態的實體創建盡可能多的你想要的,知道其他HTTP會話實體也使用相同的實體創建userPreferences bean定義看不到這些變化狀態,因為他們是特定于一個單獨的HTTP會話,當HTTP會話最終被丟棄時,作用域為該特定HTTP會話的bean也將被丟棄,
在使用注釋驅動的組件或Java配置時,您可以使用@SessionScope注釋來為會話范圍分配一個組件,
@SessionScope
@Component
public class UserPreferences {
// ...
}
Application Scope
考慮以下XML配置來定義bean:
<bean id="appPreferences" scope="application"/>
Spring容器為整個web應用程式使用一次App Preferences bean定義,從而創建AppPreferences bean的一個新實體,也就是說,appPreferences bean的作用域在ServletContext級別,并存盤為一個常規的ServletContext屬性,這有點類似于彈簧單例bean,但在兩個重要方面不同:它是一個單例每ServletContext不是每春天ApplicationContext的(可能有幾個在任何給定的web應用程式),它實際上是暴露,因此可見ServletContext屬性,
當使用注釋驅動的組件或Java配置時,您可以使用@ApplicationScope注釋來為應用程式范圍分配一個組件,
下面的例子展示了如何做到這一點:
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
將帶有Scope屬性的的bean作為依賴項
Spring IoC容器不僅管理物件(bean)的實體化,而且還管理協作者(或依賴關系)的連接,如果您想將(例如)一個HTTP請求作用域的bean注入到另一個更長壽作用域的bean中,您可以選擇注入一個AOP代理來代替作用域bean,也就是說,您需要注入一個代理物件,該代理物件公開與作用域物件相同的公共介面,但也可以從相關作用域檢索實際目標物件(例如HTTP請求),并將方法呼叫委托給實際物件,
您還可以在作用域為單例的bean之間使用<aop:scoped-proxy/>,然后通過一個可序列化的中間代理進行參考,
從而能夠在反序列化時重新獲得目標單例bean,
當對作用域原型bean宣告<aop:scoped-proxy/>時,對共享代理的每個方法呼叫都會創建一個新的目標實體,
然后將呼叫轉發到該目標實體,
而且,作用域代理并不是解決長作用域bean接收短作用域bean的唯一方法,
你也可以宣告你的注入方式(也就是建構式或setter引數或autowired的欄位)
作為ObjectFactory<MyTargetBean>,
允許getObject()呼叫來檢索當前實體對需求每次需要——沒有分別持有實體或存盤它,
例如:
@Autowired
private ObjectFactory<ServiceTwo> serviceTwo;
@RequestMapping("/b")
@ResponseBody
public String getName(){
return serviceTwo.getObject().getServiceOneName();
}
作為擴展變數,您可以宣告ObjectProvider<MyTargetBean>,
它提供了幾個額外的訪問變數,包括getIfAvailable和getIfUnique,
以下示例中的配置僅一行,但是了解其背后的“原因”和“方式”很重要:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--scope 為 session 的bean-->
<bean id="userPreferences" scope="session">
<!--增加當前標簽后 每次userService 請求該bean的實體都是請求的代理物件
代理物件再去呼叫真是實體,就可以保證短期Scope能正常使用,而不至于使短期
Scope保持和單例Scope一樣長的生命周期-->
<aop:scoped-proxy/> !!!
</bean>
<!-- scope 為單例的bean -->
<bean id="userService" >
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
要創建 !!! 處這樣的代理,需要將子元素aop:scoped-proxy/插入作用域bean定義中,
為什么在request,session和自定義Scope的bean需要aop:scoped-proxy/元素?
看下面的例子和上邊的做對比!
<bean id="userPreferences" scope="session"/>
<bean id="userManager" >
<property name="userPreferences" ref="userPreferences"/>
</bean>
在前面的示例中,單例bean (userManager)被注入了對HTTP會話作用域bean (userPreferences)的參考,
這里的重點是userManager bean是一個單例:每個容器只實體化一次,
它的依賴項(在本例中只有一個,即userPreferences bean)也只被注入一次,
這意味著userManager bean只操作完全相同的userPreferences物件(即最初注入它的那個物件,因為userPreferences并沒有達到 session Scope 應有的作用域的效果),
當將較短的作用域bean注入到較長作用域bean中時(例如,將HTTP會話作用域的協作bean作為依賴項注入到單例bean中),這不是您想要的行為,
相反,您僅僅只需要一個userManager物件,但是在HTTP會話的生命周期中,您需要一個特定于HTTP會話的userPreferences物件,
因此,容器創建一個物件,該物件公開與UserPreferences類完全相同的公共介面(理想情況下,該物件是一個UserPreferences實體),它可以從作用域機制(HTTP請求、會話等)獲取真正的UserPreferences物件,
容器將這個代理物件注入到userManager bean中,但是userManager bean不知道這個UserPreferences參考是一個代理,
在本例中,當UserManager實體呼叫依賴注入的UserPreferences物件上的方法時,它實際上是在呼叫代理上的方法,代理然后從(在本例中討論的是HTTP會話)HTTP會話中獲取真實的UserPreferences物件,并將方法呼叫委托給檢索到的真實UserPreferences物件,
因此,你當注射需要滿足以下(正確和完整)的配置 request-和session-scoped豆類為合作物件,如下例所示:
<bean id="userPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" >
<property name="userPreferences" ref="userPreferences"/>
</bean>
選擇要創建的代理型別
默認情況下,當Spring容器為標記為aop:scoped-proxy/元素的bean創建代理時,將創建一個基于cglib的類代理,
CGLIB代理僅攔截公共方法呼叫!不要在這樣的代理上呼叫非公共方法,它們沒有被委托給實際作用域的目標物件,
或者,您可以配置Spring容器,通過為aop:scoped-proxy/元素的proxy-target-class屬性的值指定false,為這種作用域bean創建標準的基于JDK介面的代理,
aop:scoped-proxy/使用JDK基于介面的代理意味著您不需要在應用程式類路徑中添加其他庫即可使用這種代理,
然而,這也意味著作用域bean的類必須實作至少一個介面,并且所有注入作用域bean的協作者必須通過它的其中一個介面參考該bean,
以下示例顯示基于介面的代理:proxy-target-classaop:scoped-proxy/
<bean id="userPreferences" scope="session">
<aop:scoped-proxy proxy-target-/>
</bean>
<bean id="userManager" >
<property name="userPreferences" ref="userPreferences"/>
</bean>
1.5.5,自定義作用域
Bean作用域機制是可擴展的,您可以定義自己的作用域,甚至重新定義現有作用域,盡管后者被認為是不好的做法,并且您不能覆寫內置作用域singleton和prototype作用域,
創建自定義范圍
要將您的自定義作用域集成到Spring容器中,您需要實作org.springframework.bean .factory.config.Scope介面,
Scope介面有四個方法,用于從范圍中獲取物件、從范圍中洗掉物件以及銷毀它們,
例如,session scope實作回傳session scope的bean(如果它不存在,則在將其系結到會話以供將來參考之后,該方法回傳該bean的一個新實體),下面的方法從底層范圍回傳物件:
Object get(String name, ObjectFactory<?> objectFactory)
session scope的實作,例如,從基礎會話中洗掉了session scope的bean并回傳該物件,
如果找不到具有指定名稱的物件,則可以回傳null,以下方法從基礎范圍中洗掉該物件:
Object remove(String name)
以下方法注冊一個回呼,當銷毀作用域或銷毀作用域中的指定物件時,作用域應呼叫該回呼:
void registerDestructionCallback(String name, Runnable destructionCallback)
以下方法獲取基礎范圍的會話識別符號:
String getConversationId()
每個范圍的識別符號都不相同,對于會話范圍的實作,此識別符號可以是會話識別符號,
使用自定義范圍
在撰寫和測驗一個或多個自定義Scope實作之后,您需要使Spring容器意識到您的新作用域,以下方法是Scope在Spring容器中注冊新方法的主要方法:
void registerScope(String scopeName, Scope scope);
此方法在ConfigurableBeanFactory介面上宣告,該介面可通過Spring附帶的大多數具體ApplicationContext實作上的BeanFactory屬性使用,
registerScope(..)方法的第一個引數是與作用域關聯的惟一名稱,此類名稱在Spring容器本身中的例子有singleton和prototype,
registerScope(..)方法的第二個引數是您希望注冊和使用的自定義范圍實作的一個實際實體,
假設您撰寫了自定義范圍實作,然后注冊它,如下一個示例所示,
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
然后,您可以按照您的custom的作用域規則創建bean定義, Scope如下所示:
<bean id="..." scope="thread">
使用自定義Scope實作,您不僅僅局限于按部就班式的注冊bean,
您還可以Scope使用CustomScopeConfigurer該類以宣告方式進行注冊 ,如以下示例所示:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean >
<property name="scopes">
<map>
<entry key="thread">
<bean />
</entry>
</map>
</property>
</bean>
<bean id="thing2" scope="thread">
<property name="name" value="https://www.cnblogs.com/dabaieyangzhijidi/p/Rick"/>
<aop:scoped-proxy/>
</bean>
<bean id="thing1" >
<property name="thing2" ref="thing2"/>
</bean>
</beans>
注意:
當您放置aop:scoped-proxy/在FactoryBean實作中時,作用域是工廠Bean本身,而不是從中回傳的物件getObject(),
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/104936.html
標籤:Java
上一篇:springcloud vue.js 微服務分布式 flowable 作業流 前后分離 集成代碼生成器 shiro權限
