3.9 Scope

Spring uses the scope of a bean to control how and when it is instantiated. Spring provides 5 different scopes. The scopes available are singleton, prototype, request, session, and global session. The scopes singleton and prototype are available for any bean definition and the other three are available only for web-aware ApplicationContexts. The following table shows the different scopes and where they can be used.

Table 3.1. Scope Types

ScopeContainer TypeDescription
singletonAny Only one instance of the class representing a bean definition for each ApplicationContext. This is the default bean scope.
prototypeAny A new instance of the class representing a bean definition every time it is referenced.
requestServlet Container A new instance for each HTTP request and is only available in a web-aware ApplicationContext in a servlet container.
sessionServlet Container A new instance of the class for each new HttpSession created in a servlet container and is only available in a web-aware ApplicationContext in a servlet container.
globalSessionServlet Container A new instance for each global session. This is normally only used in a portlet context and is only available in a web-aware ApplicationContext in a servlet container.


The singleton scope creates only one instance of a class for each bean in a BeanFactory. If there are multiple bean definitions for the same class, each one will have it's own instance of the class. The concept of the singleton scope varies from the singleton design pattern since that enforces one instance of a class for each classloader.

The prototype scope creates one instance of each class every time the bean definition is reference. A prototype bean only participates in initialization lifecycle events. Once it is injected or created upon a request, Spring no longer manages the bean. When injecting prototype beans into singleton beans, the prototype beans are created when the singleton bean is initialized and from that point on Spring doesn't create new ones or manage the prototype beans in any way.

The request scope creates an instance of each class for each HTTP request in the servlet container. If they are used in an ApplicationContext that doesn't support them an IllegalStateException will be thrown during initialization. The request and session scopes will be covered later in more detail in the web tier section of the book. The global session scope will not be covered since this book doesn't cover portlet technology.

Example 3.37. Singleton & Prototype Scope

The first bean definition doesn't set the scope attribute. So it will default to the scope of 'singleton'. The second bean definition sets the scope attribute to 'prototype'.

Excerpt from chapter03-scope/src/main/resources/applicationContext.xml
                
<bean id="singletonCounter" 
      class="org.springbyexample.springindepth.chapter03.scope.Counter" />

<bean id="prototypeCounter" 
      class="org.springbyexample.springindepth.chapter03.scope.Counter" 
      scope="prototype" />
                
            

Annotation-based Configuration

New Feature in Spring 2.5

When defining beans using annotations, you'll also sometimes need to define their scope. It's really simple. Only an @Scope annotation needs to be added to the class. Everything works exactly as when a bean is defined in the XML configuration. If scope isn't explicitly declared, it will default to being a singleton. Any other valid scope in the IoC container can be passed into the annotation. To set a bean to have the scope of prototype you would add @Scope(“prototype”).

Example 3.38. Annotation-based Scope

Both classes extend Counter used by the singleton and prototype beans in the XML test. The singleton example only defines the @Component annotation. Since it doesn't pass a value into it, it uses the class name as the bean name when it's registered with the IoC container.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/context/SingletonCounter.java
                    
/**
 * Annotation-based configuration of singleton <code>Counter</code>.
 */
@Component
public class SingletonCounter extends Counter {
}
                    
                

The PrototypeCounter class is a prototype bean because of the @Scope(“prototype”) annotation set on it.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/context/PrototypeCounter.java
                    
/**
 * Annotation-based configuration of prototype <code>Counter</code>.
 */
@Component
@Scope("prototype")
public class PrototypeCounter extends Counter {
}
                    
                

This configuration file is quite different compared to the other one defining the singletonCounter and prototypeCounter beans as bean elements in the XML. Notice that the context namespace was added to the XML declaration. Only the base package we want Spring to scan for annotated classes needs to be set on context:component-scan.

chapter03-scope/src/main/resources/context-applicationContext.xml
                    
<?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:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd">
               
    <!-- Scan context package for any eligible annotation configured beans. -->
    <context:component-scan base-package="org.springbyexample.springindepth.chapter03.scope.context" />

</beans>
                    
                

Custom Scopes

Spring also supports creating custom scopes by creating your own scope manager that implements the org.springframework.beans.factory.config.Scope interface. A custom scope can be registered in your Spring configuration using the CustomScopeConfigurer. Also most implementations of bean factories implement ConfigurableBeanFactory which has the method registerScope(String scopeName, Scope scope) to register a custom scope programatically. The IoC container will delegate to the custom Scope interface giving it control over whether or not to get a new bean reference or reuse one if the scope is still valid.

Example 3.39. Custom Thread Scope

This example creates a custom thread scope. This excerpt from the ThreadScope implementation shows the key Scope implementation methods of get(String, ObjectFactory), remove(String), and registerDestructionCallback(String, Runnable). The get method gets the current bean Map from ThreadScopeAttributes. This is the Map used to cache beans during the life of a thread scoped bean. If the bean being requested is in the Map, it will be returned. Otherwise the ObjectFactory will be used to create the bean, store it in the Map, and return it. The remove method can remove a bean from the cache and the registerDestructionCallback method registers any bean that should receive a destruction callback.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/ThreadScope.java
                    
/**
 * Gets bean from scope.
 */
public Object get(String name, ObjectFactory factory) {
    Object result = null;
    
    Map<String, Object> hBeans = ThreadScopeContextHolder.currentThreadScopeAttributes().getBeanMap();
    
    if (!hBeans.containsKey(name)) {
        result = factory.getObject();
        
        hBeans.put(name, result);
    } else {
        result = hBeans.get(name);
    }
    
    
    return result;
}

/**
 * Removes bean from scope.
 */
public Object remove(String name) {
    Object result = null;
    
    Map<String, Object> hBeans = ThreadScopeContextHolder.currentThreadScopeAttributes().getBeanMap();

    if (hBeans.containsKey(name)) {
        result = hBeans.get(name);
        
        hBeans.remove(name);
    }

    return result;
}

public void registerDestructionCallback(String name, Runnable callback) {
    ThreadScopeContextHolder.currentThreadScopeAttributes().registerRequestDestructionCallback(name, callback);
}
                    
                

The ThreadScopeContextHolder contains methods for accessing the ThreadScopeAttributes. The ThreadScopeAttributes is stored in a ThreadLocal. This provides a thread-local variable that is unique to each thread. Each thread will have it's own ThreadScopeAttributes and since only one thread at a time can access the Map there isn't any need to worry about synchronization issues. When ThreadLocal is initialized, the initialValue() method is overridden to return an instance of ThreadScopeAttributes for the ThreadLocal to store.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/custom/ThreadScopeContextHolder.java
                    
public class ThreadScopeContextHolder {
    
    private static final ThreadLocal<ThreadScopeAttributes> threadScopeAttributesHolder = 
	new InheritableThreadLocal<ThreadScopeAttributes>() {
        protected ThreadScopeAttributes initialValue() {
            return new ThreadScopeAttributes();
        }
    };

    /**
     * Gets <code>ThreadScopeAttributes</code>.
     */
    public static ThreadScopeAttributes getThreadScopeAttributes() {
        return threadScopeAttributesHolder.get();
    }

    /**
     * Sets <code>ThreadScopeAttributes</code>.
     */
    public static void setThreadScopeAttributes(ThreadScopeAttributes accessor) {
        ThreadScopeContextHolder.threadScopeAttributesHolder.set(accessor);
    }

    /**
     * Gets current <code>ThreadScopeAttributes</code>.
     */
    public static ThreadScopeAttributes currentThreadScopeAttributes() throws IllegalStateException {
        ThreadScopeAttributes accessor = threadScopeAttributesHolder.get();
        
        if (accessor == null) {
            throw new IllegalStateException("No thread scoped attributes.");
        }
        
        return accessor;
    }

}
                    
                

ThreadScopeAttributes contains a cache for thread scoped beans and also maintains what beans should have destruction callbacks processed when the scope ends. There is also a clear() method for processing any beans registered for destruction callbacks and clearing the cache.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/custom/ThreadScopeAttributes.java
                    
public class ThreadScopeAttributes {
    
    final Logger logger = LoggerFactory.getLogger(ThreadScopeAttributes.class);
    
    protected final Map<String, Object> hBeans = new HashMap<String, Object>();
    protected final Map<String, Runnable> hRequestDestructionCallbacks = new LinkedHashMap<String, Runnable>();

    /**
     * Gets bean <code>Map</code>.
     */
    protected final Map<String, Object> getBeanMap() {
        return hBeans;
    }

    /**
     * Register the given callback as to be executed after request completion.
     * 
     * @param   name        The name of the bean.
     * @param   callback    The callback of the bean to be executed for destruction.
     */
    protected final void registerRequestDestructionCallback(String name, Runnable callback) {
        Assert.notNull(name, "Name must not be null");
        Assert.notNull(callback, "Callback must not be null");
        
        hRequestDestructionCallbacks.put(name, callback);
    }

    /**
     * Clears beans and processes all bean destruction callbacks.
     */
    protected final void clear() {
        processDestructionCallbacks();
        
        hBeans.clear();   
    }

    /**
     * Processes all bean destruction callbacks.
     */
    private final void processDestructionCallbacks() {
        for (String name: hRequestDestructionCallbacks.keySet()) {
            Runnable callback = hRequestDestructionCallbacks.get(name);
            
            logger.debug("Performing destruction callback for '" + name + "' bean" + 
                     " on thread '" + Thread.currentThread().getName() + "'.");
            
            callback.run();
        }
        
        hRequestDestructionCallbacks.clear();
    }

}
                    
                

A lot of the time you will probably be using thread scopes with a pool of threads. In that case you really want any thread scoped beans to be cleared when when the current Runnable finishes. The ThreadScopeRunnable wraps another Runnable and after it finishes running, processes any destruction callbacks and also clears any cached beans.

Excerpt from chapter03-scope/src/main/java/org/springbyexample/springindepth/chapter03/scope/custom/ThreadScopeRunnable.java
                    
public class ThreadScopeRunnable implements Runnable {
    
    protected Runnable target = null;

    /**
     * Constructor
     */
    public ThreadScopeRunnable(Runnable target) {
        this.target = target;
    }

    /**
     * Runs <code>Runnable</code> target and 
     * then afterword processes thread scope 
     * destruction callbacks.
     */
    public final void run() {
        try {
            target.run();
        } finally {
            ThreadScopeContextHolder.currentThreadScopeAttributes().clear();
        }
    }
    
}
                    
                

The first bean definition creates a thread scoped counter. The second bean definition creates another thread scoped counter that implements the DisposableBean interface. The third bean definition creates a thread scoped bean that uses the bean element's destroy-method attribute. The second and third bean will both have destruction callbacks registered with ThreadScope. So when they are run in a ThreadScopeRunnable their destruction methods will be processed.

The fourth bean definition registers the custom thread scope using the CustomScopeConfigurer. Notice that the key's value is 'thread', which can then be used by other beans to signify this scope. The last definition is the thread pool used in some of the unit tests.

Excerpt from chapter03-scope/src/main/resources/custom-applicationContext.xml
                    
<bean id="threadCounter" 
      class="org.springbyexample.springindepth.chapter03.scope.Counter" 
      scope="thread" />

<bean id="disposableThreadCounter" 
      class="org.springbyexample.springindepth.chapter03.scope.DisposableCounter" 
      scope="thread" />

<bean id="xmlDisposableThreadCounter" 
      class="org.springbyexample.springindepth.chapter03.scope.Counter" 
      scope="thread" 
      destroy-method="reset" />

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="thread">
                <bean class="org.springbyexample.springindepth.chapter03.scope.custom.ThreadScope"/>
            </entry>
        </map>
    </property>
</bean>

<bean id="taskExecutor"
    class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="10" />
    <property name="queueCapacity" value="30" />
    <property name="threadFactory">
       <bean class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
            <property name="threadNamePrefix" value="taskExecutor-" />
       </bean>
    </property>
</bean>
                    
                

When creating a request or session scoped bean, it will need to have the aop:scoped-proxy element inside the bean element. An AOP proxy will then actually wrap bean when it is injected into another bean. Then each time the bean makes a method call to the proxy to it the first time in a new request or session scope lifecycle, the AOP proxy will retrieve a new instance of the bean to use. The CGLIB library will be used by default to create the proxy instance, but if you would rather Java Dynamic Proxies were used the proxy-target-class attribute on the aop:scoped-proxy element can be set to false.