The IoC Container 1.5

1.5. Bean Scopes

Spring可以帮助我们装配对象,从而促使系统工作。另外,Spring还可以帮助我们管理对象的生命周期,Spring称之为Bean Scopes。Spring支持6种Scope,其中4种只能用于spring web。
Bean scopes

Scope Description
singleton 单例
prototype 每次使用都生成新实例,实例数目不限
request 每次HTTP请求时生成实例,单个请求中为单例
session 单个HTTP session中为单例
application 单个ServletContext中为单例
webSocket 单个websocket中为单例

后四种scope针对web应用。

1.5.1. The Singleton Scope

单例(很基础,不多说了):


singleton scope

Spring中的单例跟设计模式中的单例稍微有点区别的地方在于,Spring中的单例是per-container and per-bean(IoC Container中的单例)。Spring中的默认scope是singleton,所以如果不指定bean的scope,那么获取的一定是在Spring中全局唯一的那个单例。
xml配置示例:





1.5.2. The Prototype Scope

prototype

注意,请不要被上图中的accountDao迷惑,因为accountDao一定是典型的单例,这块只是为了复用之前的单例图:

(A data access object (DAO) is not typically configured as a prototype, because a typical DAO does not hold any conversational state. It was easier for us to reuse the core of the singleton diagram.)

使用prototype scope的原则:

As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.

配置示例:


关于prototype,需要注意的是,Spring只会每次生成实例后就将实例交给用户,用户需要负责销毁实例和释放实例资源的工作,而Spring只负责初始化实例时的工作。prototype在某种程度上可以等价为java的new操作,但是new完之后实例的生命周期需要由用户控制。本节提到可以通过实现bean post-processor来让Spring管理prototype scope对象的生命周期,不过本节没有说明如何实现。

1.5.3. Singleton Beans with Prototype-bean Dependencies

在Spring中如果试图将Prototype-bean注入到Singleton Bean中,那么只会注入一次,不会发生每次使用单例时都给用户重新生成Prototype-bean的事情。如果真的有这种需求,那么可以参考Method Injection。

1.5.4. Request, Session, Application, and WebSocket Scopes

request, session, application, and websocket scopes。这四种scope必须在web类型的ApplicationContext(比如XmlWebApplicationContext)下才能正常工作,假设使用常规的ClassPathXmlApplicationContext容器,那么直接报IllegalStateException异常并提示错误。


非法的scope

由于还没看过spring web方面的文档,此节后续再补充。

1.5.5. Custom Scopes

用户自定义scope。Spring支持用户自定义scope类型,甚至可以覆盖重写原有类型,当然,覆盖重写原有类型Spring官方是不推荐的。
实现用户自定义scope,需要继承实现Scope接口。

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.lang.Nullable;

public interface Scope {
    Object get(String var1, ObjectFactory var2);

    @Nullable
    Object remove(String var1);

    void registerDestructionCallback(String var1, Runnable var2);

    @Nullable
    Object resolveContextualObject(String var1);

    @Nullable
    String getConversationId();
}

接口的文档说明可以参考:
Scope接口

官方文档建议直接看源码的具体实现。比如RequestScope。

package org.springframework.web.context.request;

import org.springframework.lang.Nullable;

public class RequestScope extends AbstractRequestAttributesScope {
    public RequestScope() {
    }

    protected int getScope() {
        return 0;
    }

    @Nullable
    public String getConversationId() {
        return null;
    }
}

RequestScope没什么具体实现,干货都在AbstractRequestAttributesScope里面。getScope返回0,这个应该是类似用来枚举不同的scope,不同的值代表不同的scope类型,0代表request scope。

package org.springframework.web.context.request;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.lang.Nullable;

public abstract class AbstractRequestAttributesScope implements Scope {
    public AbstractRequestAttributesScope() {
    }

    public Object get(String name, ObjectFactory objectFactory) {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        Object scopedObject = attributes.getAttribute(name, this.getScope());
        if (scopedObject == null) {
            scopedObject = objectFactory.getObject();
            attributes.setAttribute(name, scopedObject, this.getScope());
            Object retrievedObject = attributes.getAttribute(name, this.getScope());
            if (retrievedObject != null) {
                scopedObject = retrievedObject;
            }
        }

        return scopedObject;
    }

    @Nullable
    public Object remove(String name) {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        Object scopedObject = attributes.getAttribute(name, this.getScope());
        if (scopedObject != null) {
            attributes.removeAttribute(name, this.getScope());
            return scopedObject;
        } else {
            return null;
        }
    }

    public void registerDestructionCallback(String name, Runnable callback) {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        attributes.registerDestructionCallback(name, callback, this.getScope());
    }

    @Nullable
    public Object resolveContextualObject(String key) {
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        return attributes.resolveReference(key);
    }

    protected abstract int getScope();
}

重要方法详解:
Object get(String name, ObjectFactory objectFactory)
用于获取bean,代码很简单,一看就懂,RequestScope的实现中,最终将生成的bean对象保存在RequestContextHolder类中名为requestAttributesHolder成员变量中,requestAttributesHolder的类型为ThreadLocal,ThreadLocal保证了线程中的bean唯一,从而才保证了bean是request scope级别。讲的更清楚点:相当于每次请求所在的线程中,该对象为单例。根据Spring之前给Container的单例解释,我给request scope中的bean取了个类似的英文名叫做per-thread and per-bean。

Object remove(String name)
get方法的反向操作。

void registerDestructionCallback(String name, Runnable destructionCallback)
注册回调,负责对象的一些销毁工作,同样是注册到ThreadLocal中。

Using a Custom Scope
当用户自定义scope实现完成之后,必须注册后才能使用。SimpleThreadScope是Spring自带的scope类型实现,但是默认不会注册,下面代码示例将会把这个scope注册进去。

ApplicationContext context = new ClassPathXmlApplicationContext( "scope.xml");
((ClassPathXmlApplicationContext) context).getBeanFactory().registerScope("thread", new SimpleThreadScope());

上面示例是代码的注册方式,还可以使用xml的配置注册方式,如下:

    
        
            
                
                    
                
            
        
    

使用的话,跟指定Spring自带的scope没什么两样:


总结一下,Spring默认支持6种scope,其中4种scope必须在web类型的Container中才能使用,同时还支持用户自定义scope。

你可能感兴趣的:(The IoC Container 1.5)