概述
Bean的作用域:
什么是作用域呢?即“scope”,在面向对象程序设计中一般指对象或变量之间的可见范围。而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围。
Spring支持以下作用域:
- 基本作用域:singleton、prototype
- web作用域 :request、session、global session
- 自定义作用域
详析
基本作用域
Singleton作用域
当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,每次请求该bean都会返回bean的同一实例。
换言之,当把一个bean定义设置为singlton作用域时,Spring IoC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都将返回被缓存的对象实例。
Prototype
Prototype作用域的bean会导致在每次请求该id的Bean时都会创建一个新的bean实例,然后返回给程序。这种情况下,spring容器仅仅使用new关键字创建Bean实例,一旦创建成功,容器不再维护Bean实例的状态。
demo
xml配置
<bean class="com.lili.scope.service.SingletonService" scope="singleton" id="singletonService" />
<bean class="com.lili.scope.service.PrototypeScopeService" scope="prototype" id="prototypeScopeService"/>
@RestController
@RequestMapping("/service")
public class ScopeController {
@RequestMapping(value="/scope",method = RequestMethod.GET)
public void singletonReuest(){
System.out.println("singletonService===="+RuntimeContext.getBean("singletonService"));
System.out.println("prototypeScopeService====1====="+RuntimeContext.getBean("prototypeScopeService"));
System.out.println("prototypeScopeService====2====="+RuntimeContext.getBean("prototypeScopeService"));
}
}
两次请求结果:
/*********第一次请求*****************/
singletonService====com.lili.scope.service.SingletonService@5c34c8da
prototypeScopeService====1=====com.lili.scope.service.PrototypeScopeService@4a055767
prototypeScopeService====2=====com.lili.scope.service.PrototypeScopeService@44c2b057
/*********第二次请求*****************/
singletonService====com.lili.scope.service.SingletonService@5c34c8da
prototypeScopeService====1=====com.lili.scope.service.PrototypeScopeService@3e571966
prototypeScopeService====2=====com.lili.scope.service.PrototypeScopeService@68655866
我们可以从结果中发现,两次请求中singletonService的对象是同一个,同一个请求中的两次调用prototypeScopeService都不是同一个对象。
web作用域
request、session作用域只在web应用中才有效,并且必须在web应用中增加相应配置才会生效。为了让request、session作用域生效,必须把HTTP请求对象绑定到为该请求提供服务的线程上,使得具有request和session作用域的Bean实例能够在后面的调用中被访问。
Request作用域
考虑下面bean定义:
针对每次HTTP请求,Spring容器会创建一个全新的bean实例,且该bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中获得的 bean实例不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
Session作用域
考虑下面bean定义:
Session作用域与request作用域一样,区别在于:request作用域的Bean对于每次HTTP请求有效,而session作用域的Bean则对应每次HTTP Session有效。
gloable session作用域
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
demo
xml配置
<bean class="com.lili.scope.service.RequestScopeService" scope="request" id="requestScopeService" />
<bean class="com.lili.scope.service.SessionScopeService" scope="session" id="sessionScopeService"/>
测试代码
@RequestMapping(value="/requestScope",method = RequestMethod.GET)
public void requestScope(){
System.out.println("requestScopeService===="+RuntimeContext.getBean("requestScopeService"));
System.out.println("sessionScopeService===="+RuntimeContext.getBean("sessionScopeService"));
}
请求结果:
/*********第一次请求*****************/
requestScopeService====com.lili.scope.service.RequestScopeService@2e945244
sessionScopeService====com.lili.scope.service.SessionScopeService@4fbb381e
/*********第二次请求*****************/
requestScopeService====com.lili.scope.service.RequestScopeService@68f187e7
sessionScopeService====com.lili.scope.service.SessionScopeService@4fbb381e
/*********第三次请求*****************/
requestScopeService====com.lili.scope.service.RequestScopeService@3682c6a7
sessionScopeService====com.lili.scope.service.SessionScopeService@4fbb381e
第一、第二次请求在同一个浏览器,也就是同一个session,第三次请求在新的浏览器请求:不同浏览器(不同session)返回的 Bean 实例不同,而同一个浏览器(同一会话)多次发起请求返回的是同一个 Bean 实例。
自定义作用域
scope接口
public interface Scope {
// 从作用域中获取Bean,先暴露ObjectFactory对象,解决了循环依赖的问题,
// bean的实际创建是ObjectFactory#getObject()
Object get(String name, ObjectFactory<?> objectFactory);
// 从作用域中移除 Bean
Object remove(String name);
// 用于注册销毁回调,如果想要销毁相应的对象则由Spring容器注册相应的销毁回调,而由自定义作用域选择是不是要销毁相应的对象;
void registerDestructionCallback(String name, Runnable callback);
//用于解析相应的上下文数据,比如request作用域将返回request中的属性。
Object resolveContextualObject(String key);
// 作用域的会话标识,比如session作用域将是sessionId。
String getConversationId();
}
示例demo
xml配置
<!-- CustomScopeConfigurer 的 scopes 属性注册自定义作用域实现 -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread" >
<bean class="com.lili.scope.CustomerScope"/>
</entry>
</map>
</property>
</bean>
<bean class="com.lili.scope.service.CustomerScopeService" scope="thread" id="customerScopeService" />
自定义的scope类
/**
* @Autor: lili
* @Date: 2018/3/30-14:56
* @Description: 自定义scope,作用范围同一个Thread
*/
public class CustomerScope implements Scope{
private static final ThreadLocal<Map<String,Object>> THREAD_BEAN_MAP = new ThreadLocal<Map<String, Object>>(){
//初始化自定义上下文数据
protected Map<String,Object> initialValue(){
return new HashMap<>();
}
};
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String,Object> beanMap = THREAD_BEAN_MAP.get();
if(!beanMap.containsKey(name)){
beanMap.put(name,objectFactory.getObject());
}
return beanMap.get(name);
}
@Override
public Object remove(String name) {
Map<String,Object> beanMap = THREAD_BEAN_MAP.get();
return beanMap.remove(name);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public String getConversationId() {
return null;
}
}
调用代码:
@RequestMapping(value="/threadScope",method = RequestMethod.GET)
public void Scope() throws InterruptedException {
System.out.println("customerScopeService===="+RuntimeContext.getBean("customerScopeService"));
// 新建线程
new Thread(){ public void run() {
System.out.println("customerScopeService in new Thread ===="+RuntimeContext.getBean("customerScopeService"));
}}.start();
Thread.sleep(1000);
System.out.println("customerScopeService===="+RuntimeContext.getBean("customerScopeService"));
}
请求结果:
customerScopeService====com.lili.scope.service.CustomerScopeService@1c6a2bcf
customerScopeService in new Thread ====com.lili.scope.service.CustomerScopeService@2948c3e6
customerScopeService in new Thread ====com.lili.scope.service.CustomerScopeService@2948c3e6
customerScopeService====com.lili.scope.service.CustomerScopeService@1c6a2bcf
我们可以发现在同一个Thread中的customerScopeService对象是同一个。
scope源码分析
scope#get()方法的调用
在bean的实例化过程中会对bean的scope进行判断,下面看一下AbstractBeanFactory#doGetBean()方法,这里的代码省略了很多,具体的初始化可以看一下《Spring Bean的初始化》。
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//.......
}
else {
//.......
try {
//.....
// Create bean instance.
//单例,缓存
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//原型 直接创建一个新的对象
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//web或自定义的scope
else {
//取出相应的scope实例,如:RequestScope、SessionScope等
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
//scope的get方法具体调用,下面可以具体分析不同的Scope
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
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);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// .....省略
return (T) bean;
}
scope继承结构
AbstractRequestAttributesScope
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
// RequestContextHolder中通过ThreadLocal将RequestAttributes实例和当前线程绑定
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
//根据scope的不同,进行不同的操作,具体方法在ServletRequestAttributes中
Object scopedObject = attributes.getAttribute(name, getScope());
// 从当前线程绑定的RequestAttributes中获取对象
// 如果已经实例化过了,则不再实例化
// 否则进入Spring#createBean()获取对象
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
attributes.setAttribute(name, scopedObject, getScope());
}
return scopedObject;
}
ServletRequestAttributes#getAttribute
@Override
public Object getAttribute(String name, int scope) {
if (scope == SCOPE_REQUEST) {
if (!isRequestActive()) {
throw new IllegalStateException(
"Cannot ask for request attribute - request is not active anymore!");
}
return this.request.getAttribute(name);
}
else {
HttpSession session = getSession(false);
if (session != null) {
try {
Object value = session.getAttribute(name);
if (value != null) {
this.sessionAttributesToUpdate.put(name, value);
}
return value;
}
catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
return null;
}
}
RequestContextHolder
public abstract class RequestContextHolder {
private static final boolean jsfPresent =
ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
/**
* Reset the RequestAttributes for the current thread.
*/
public static void resetRequestAttributes() {
requestAttributesHolder.remove();
inheritableRequestAttributesHolder.remove();
}
/**
* Bind the given RequestAttributes to the current thread,
*/
public static void setRequestAttributes(RequestAttributes attributes) {
setRequestAttributes(attributes, false);
}
/**
* Bind the given RequestAttributes to the current thread.
*/
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
/**
* Return the RequestAttributes currently bound to the thread.
*/
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
/**
* Return the RequestAttributes currently bound to the thread.
*/
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
throw new IllegalStateException(".....");
}
}
return attributes;
}
/**
* Inner class to avoid hard-coded JSF dependency.
*/
private static class FacesRequestAttributesFactory {
public static RequestAttributes getFacesRequestAttributes() {
FacesContext facesContext = FacesContext.getCurrentInstance();
return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
}
}
}
RequestScope与SessionScope
public class RequestScope extends AbstractRequestAttributesScope {
@Override
protected int getScope() {
return RequestAttributes.SCOPE_REQUEST;
}
/**
* There is no conversation id concept for a request, so this method
* returns {@code null}.
*/
@Override
public String getConversationId() {
return null;
}
}
public class SessionScope extends AbstractRequestAttributesScope {
private final int scope;
public SessionScope(boolean globalSession) {
this.scope = (globalSession ? RequestAttributes.SCOPE_GLOBAL_SESSION : RequestAttributes.SCOPE_SESSION);
}
@Override
protected int getScope() {
return this.scope;
}
@Override
public String getConversationId() {
return RequestContextHolder.currentRequestAttributes().getSessionId();
}
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
synchronized (mutex) {
return super.get(name, objectFactory);
}
}
@Override
public Object remove(String name) {
Object mutex = RequestContextHolder.currentRequestAttributes().getSessionMutex();
synchronized (mutex) {
return super.remove(name);
}
}
}
SessionScope和RequestScope都是AbstractRequestAttributesScope的子类,获取对象的方式都一样,不同的是生命周期以及SessionScope#get方法加了锁synchronized。
scope实例的注册
AbstractApplicationContext#refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// spring 中RequestScope SessionScope等Scope在这里完成注册
postProcessBeanFactory(beanFactory);
//自定义的Scope的注册是在这里完成注册的
invokeBeanFactoryPostProcessors(beanFactory);
//.....省略
}
catch (BeansException ex) {
//........省略
}
finally {
//.....省略
}
}
}
AbstractRefreshableWebApplicationContext#postProcessBeanFactory
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
//
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}
WebApplicationContextUtils#registerWebApplicationScopes
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
// Register as ServletContext attribute, for ContextCleanupListener to detect it.
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
if (jsfPresent) {
FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
}
}
自定义scope实例的注册
CustomScopeConfigurer#postProcessBeanFactory
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.scopes != null) {
for (Map.Entry<String, Object> entry : this.scopes.entrySet()) {
String scopeKey = entry.getKey();
Object value = entry.getValue();
if (value instanceof Scope) {
beanFactory.registerScope(scopeKey, (Scope) value);
}
else if (value instanceof Class) {
Class<?> scopeClass = (Class<?>) value;
Assert.isAssignable(Scope.class, scopeClass);
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else if (value instanceof String) {
Class<?> scopeClass = ClassUtils.resolveClassName((String) value, this.beanClassLoader);
Assert.isAssignable(Scope.class, scopeClass);
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else {
throw new IllegalArgumentException("Mapped value [" + value + "] for scope key [" +
scopeKey + "] is not an instance of required type [" + Scope.class.getName() +
"] or a corresponding Class or String value indicating a Scope implementation");
}
}
}
}