主頁 > 後端開發 > 一起來讀官方檔案-----SpringIOC(05)

一起來讀官方檔案-----SpringIOC(05)

2020-09-22 13:26:30 後端開發

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權限

下一篇:自學Java最起碼要學到什么程度才能出去找作業?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more