Spring : creating unique beans per thread

In stand-alone non-web Java applications, using Spring, the most commonly used bean scopes are singleton and prototype. If a bean scope is set to singleton (and by default bean scope is always singleton), the Spring container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of such singleton beans, and all subsequent requests and references for that named bean return the cached object. This means if we want to use that bean across multiple threads, either the bean should be stateless or the bean's state should be read and altered under the protection of a memory barrier enforcing happens before guarantee - which is a massive performance killer. We can use prototype scope instead, but that would mean creation of a new object in memory for every request to applicationContext. If our application deals with millions of short-life requests, then creating millions of objects (which will be garbage collected almost immediately) doesn't sound like like a good idea.

Ideally what we need is something like a thread local scope - where requests coming from same thread would result in the same cached object being returned but if request comes from a new thread, a new object should be created.

Spring allows us to build our own custom scopes and register them with application context. To define a custom scope, we need to write a class implementing the following interface :

public interface Scope {
     Object get(String name, ObjectFactory<?> objectFactory);
     Object remove(String name);
     void registerDestructionCallback(String name, Runnable callback);
     Object resolveContextualObject(String key);
     String getConversationId();
}

Spring already provides us an implementation of Scope interface which provides us with ThreadLocal -like semantics - the org.springframework.context.support.SimpleThreadScope class. As you might have already guessed, the SimpleThreadScope class maintains it's internal state in the form of a ThreadLocal object

private final ThreadLocal<Map<String, Object>> threadScope = new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
    @Override
    protected Map<String, Object> initialValue() {
        return new HashMap<String, Object>();
    }
};

Note that SimpleThreadScope class is available as part of Spring dependency jars but it is not registered as an available scope. In order to use it, we need to register it first. One easy way to register it is by writing a custom BeanFactoryPostProcessor :

class DummyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerScope("threadlocal", new SimpleThreadScope());    
    }   
}

To make it even easier, Spring already comes with a built-in BeanFactoryPostProcessor that registers custom scopes - the org.springframework.beans.factory.config.CustomScopeConfigurer class. CustomScopeConfigurer provides API to register a custom scope :

public void addScope(String scopeName, Scope scope) {
    if (this.scopes == null) {
        this.scopes = new LinkedHashMap<String, Object>(1);
    }
    this.scopes.put(scopeName, scope);
}

The String argument indicates the name of the scope which will be used by beans to specify scope (either through the XML scope attribute or through the @Scope annotation).

If we have multiple custom scopes to be registered, we inject all of them as a Map:

public void setScopes(Map<String, Object> scopes) {
    this.scopes = scopes;
}

Here's an example of registering custom scope using XML based configuration :

<bean id="threadlocalScope" class="org.springframework.context.support.SimpleThreadScope"/ >

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="threadlocal" value-ref="threadlocalScope"/>
        </map>
    </property>
</bean>

Here an instance of SimpleThreadScope is being registered as a custom scope accessible through the key "threadlocal"

This custom scope can be used as part of bean definitions through XML configuration:

<bean id="foo" class="spring.xml.ioc.scope.Foo" scope="threadlocal"/>

or through annotation driven configuration:

@Bean
@Scope("threadlocal")
public Foo foo() {
    return new Foo();
}

Lets try loading the ApplicationContext and verify if its indeed working as intended:

ApplicationContext container = new ClassPathXmlApplicationContext("MyConfig.xml");
Foo foo1 = container.getBean("foo", Foo.class);
Foo foo2 = container.getBean("foo", Foo.class);    
System.out.println(foo1 == foo2);

Output:

true

We are invoking container.getBean() from the same thread, so same object is being returned

Now let's try invoking container.getBean() from two different threads :
Let's define a Runnable task which extracts the Bean from container

class MyRunnable implements Runnable {

    final ApplicationContext container;
    final String beanId;
    volatile Object bean;

    public MyRunnable(ApplicationContext container, String beanId) {
        this.container = container;
        this.beanId = beanId;
    }

    public void run() {
        bean = container.getBean(beanId);
    }

    public Object getBean() {
        return bean;
    }    
}

Now we can start two different threads each of which will internally invoke container.getBean("foo")

    MyRunnable r1 = new MyRunnable(container, "foo");
    MyRunnable r2 = new MyRunnable(container, "foo");
    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);
    t1.start();
    t2.start();
    // wait to ensure that both the threads are done
    t1.join();
    t2.join();

    Foo foo3 = (Foo) r1.getBean();
    Foo foo4 = (Foo) r2.getBean();
    System.out.println(foo3 == foo4);

Output:

 false

When we invoked getBean() with same same id from two different threads, we got two different objects as expected


comments powered by Disqus