Spring系列8:bean的作用域
本文內容
- bean定義資訊的意義
- 介紹6種bean的作用域
bean定義資訊的意義
Spring中區分下類、類定義資訊,類實例對象的概念?不容易理解,以餐館中點炒飯為例。
類: 相當於你看到菜單上炒飯這個菜品,有這個菜。
類定義資訊:相當於炒飯的烹飪法,烹飪法只有一份
類實例對象: 相當於按照上面烹飪法炒出來的一份炒飯,可以炒分多份出來。
Spring容器中創建了一個類定義資訊的,就可以根據這個定義資訊來創建個類實例對象出來,這個理解了很關鍵。Spring中不僅可以控制 bean 對象中的各種依賴項和配置值,還可以控制 bean 額作用範圍。
介紹6種bean的作用域
Spring Framework 支援6個bean的作用域,其中4個僅在web類型的 ApplicationContext 中可用。詳細見下表
| 作用域 | 描述 |
|---|---|
| singleton | (默認)將單個 bean 定義限定為每個 Spring IoC 容器的單個對象實例。 |
| prototype | 將單個 bean 定義限定為任意數量的對象實例 |
| request | 將單個 bean 定義限定為單個 HTTP 請求的生命周期。也就是說,每個 HTTP 請求都有自己的 bean 實例,該實例是在單個 bean 定義的後面創建的。僅在web類型的 ApplicationContext有效。 |
| session | 將單個 bean 定義限定為 HTTP 會話的生命周期。僅在web類型的 ApplicationContext有效。 |
| application | 將單個 bean 定義限定為 ServletContext 的生命周期。僅在web類型的 ApplicationContext有效。 |
| websocket | 將單個 bean 定義限定為 WebSocket 的生命周期。僅在web類型的 ApplicationContext有效。 |
從 Spring 3.0 開始,執行緒範圍可用,但默認情況下未註冊。
SimpleThreadScope感興趣可以詳細看下面自定義作用域。
singleton
只有一個單例 bean 的共享實例被管理,並且所有對具有與該 bean 定義匹配的一個或多個 ID 的 bean 的請求都會導致 Spring 容器返回一個特定的 bean 實例。"蠍子粑粑獨一份"。注意是一個IoC容器內。

prototype
原型作用域下每次容器都會創建一個新的 bean 實例。通常來說,對有狀態 bean適合使用原型,對無狀態 bean適合使用單例。
下面的配置節指定了bean的作用域是原型的。
<bean id="accountService" class="x.y.z.AccountService" scope="prototype"/>
與其他範圍相比,Spring 不管理原型 bean 的完整生命周期。容器實例化、配置和以其他方式組裝原型對象並將其傳遞給客戶端,由客戶端去管理。bean生命周期後面介紹。
request, session, application, webSocket
這4個在web的應用上下文中使用的作用域,暫不展開講,留給後面寫spring mvc 專門講。
自定義作用域和使用
如何自定義?
可以通過實現org.springframework.beans.factory.config.Scope自定義作用域。Scope 介面有四種方法可以從作用域中獲取對象,將它們從作用域中移除,並讓它們被銷毀。
package org.springframework.beans.factory.config;
public interface Scope {
// 從底層範圍返回具有給定名稱的對象
Object get(String name, ObjectFactory<?> objectFactory);
// 從底層範圍中刪除具有給定的對象
Object remove(String name);
// 註冊要在作用域中指定對象的銷毀時執行的回調
void registerDestructionCallback(String name, Runnable callback);
// 省略
}
打鐵要趁熱,直接上SimpleThreadScope的源碼,解析下執行緒級別作用域是如何實現的。
package org.springframework.context.support;
public class SimpleThreadScope implements Scope {
private static final Log logger = LogFactory.getLog(SimpleThreadScope.class);
// 1 bean是存放在ThreadLocal中的 綁定了當前執行緒
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<>();
}
};
// 2 查ThreadLocal,有對應bean就直接返回,沒有就創建一個放入ThreadLocal,在返回
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
scope.put(name, scopedObject);
}
return scopedObject;
}
// 3 從ThreadLocal中移除bean
@Override
@Nullable
public Object remove(String name) {
Map<String, Object> scope = this.threadScope.get();
return scope.remove(name);
}
// 4 銷毀回調
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("SimpleThreadScope does not support destruction callbacks. " +
"Consider using RequestScope in a web environment.");
}
@Override
@Nullable
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return Thread.currentThread().getName();
}
}
如何使用?
-
先將自定義的scope注入到容器中
編碼的方式註冊:
ConfigurableBeanFactory介面提供了registerScope來註冊自定義的scope。Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope);配置文件方式的註冊: 使用
CustomScopeConfigurer以聲明方式進行註冊<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="//www.springframework.org/schema/beans" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:aop="//www.springframework.org/schema/aop" xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/aop //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> </beans> -
bean配置中使用
使用方式普通的scope一樣。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="//www.springframework.org/schema/beans" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:aop="//www.springframework.org/schema/aop" xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/aop //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="thing2" class="x.y.Thing2" scope="thread"> <property name="name" value="Rick"/> <aop:scoped-proxy/> </bean> <bean id="thing1" class="x.y.Thing1"> <property name="thing2" ref="thing2"/> </bean> </beans>
總結
本文Spring中的7種作用域,以及如何自定義作用域並使用。下一篇介紹基於註解的Spring容器配置。
知識分享,轉載請註明出處。學無先後,達者為先!

