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
| Scope | Container Type | Description |
|---|---|---|
| singleton | Any |
Only one instance of the class representing a bean definition for each
ApplicationContext. This is the default bean scope.
|
| prototype | Any | A new instance of the class representing a bean definition every time it is referenced. |
| request | Servlet Container |
A new instance for each HTTP request and is only
available in a web-aware ApplicationContext in a servlet container.
|
| session | Servlet 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.
|
| globalSession | Servlet 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" />
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.
/**
* 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.
/**
* 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>
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.
/**
* 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.
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.
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.
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.
<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.