Spring-Bean作用域scope详解

概述

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继承结构

image

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");
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值