スレッドスコープ

Springframeworkのカスタムスコープ、知っておくといろいろ便利そうなので、
リファレンスで実装例がなかった "thread" スコープを実装してみました。

ThreadLocalMapはThreadLocal内にあるHashMapをMapインターフェースでラップしたもの。

package examples.scope;

import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class ThreadScope implements Scope {

    private Map<String, Object> map = new ThreadLocalMap<String, Object>();
    
    @Override
    public synchronized Object get(String s, ObjectFactory objectFactory) {
        Object obj = map.get(s);
        if (obj == null) {
            obj = objectFactory.getObject();
            map.put(s, obj);
        }
        return obj;  
    }

    @Override
    public String getConversationId() {
        return Long.toString(Thread.currentThread().getId());
    }

    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {
    }

    @Override
    public Object remove(String s) {
        Object obj = map.remove(s);
        return obj;
    }

}

bean定義とテストコード。fooがスレッドスコープ。singletonスコープのbarを経由してfooのプロパティを参照するとスレッド毎に変わってる。

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer" >
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="examples.scope.ThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>
    
    <bean id="foo" class="examples.scope.Foo" scope="thread" >
        <property name="name" value="hoge" />
        <aop:scoped-proxy />
    </bean>

    <bean id="bar" class="examples.scope.Bar">
        <property name="foo" ref="foo" />
    </bean>
    @Test
    public void testThreadScope() throws Exception {
        final Bar bar = (Bar)context.getBean("bar");
        
        Thread t1 = new Thread(new Runnable() {
            @Override public void run() {
                testThread("thread-1 hoge", bar);
            }
        }, "thread-1");
        Thread t2 = new Thread(new Runnable() {
            @Override public void run() {
                testThread("thread-2 hoge", bar);
            }
        }, "thread-2");
        Thread t3 = new Thread(new Runnable() {
            @Override public void run() {
                testThread("thread-3 hoge", bar);
            }
        }, "thread-3");
        
        t1.start();
        t2.start();
        t3.start();
        t1.join();
        t2.join();
        t3.join();
    }

    private void testThread(String mustbe, Bar bar) {
        try {
            for (int i=0; i<3; i++) {
                assertEquals(mustbe, bar.getFooName());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }