5

一連のアクションを開始するWebサービスがあります。同じリクエストから開始されたこれらのアクションはすべて、いくつかのロックとその他の情報を含むactionContextを共有しています。

これまで、このactionContextオブジェクトは、' Request 'スコープを使用するすべてのアクションでSpringによって注入されていました。

現在、これらのアクションの進化を追跡できるようにWebソケットサービスを実装しています。
webServiceは、アクションの実行を処理するスレッドを生成し、呼び出し元のアプリケーション/ユーザーにwebSocketアドレスを返す必要があります。

このアクションは、springの@asyncアノテーションを使用して実装されており、アプリケーションコンテキストで定義されているスレッドのプールで実行されます。

問題:
この新しい機能では、生成されたスレッドはリクエストではないため、「リクエスト」スコープは機能しなくなります(Springは実行をブロックします)。

この問題を処理するための最良の解決策は何ですか?

  • スレッドスコープを実装してactionContextを処理し、すべてのアクションに正しく挿入しますか?
  • actionContextを手動でどこにでも渡します(私の意見では見栄えがよくありません)
  • webSocketをインスタンス化するwebServiceを実装し、呼び出し元にこれを最初に呼び出してから、その参照を実際のwebServiceに渡すように要求しますか?

助けてくれてありがとう!

4

1 に答える 1

6

私はすべてを可能な限りクリーンに保ち、TreadScopeの実装に進むことにしました!

私のソリューションは次のように構成されています。

  • ThreadScope同じスレッドで実行されているすべてのアクション内に同じBeanを注入するために使用されます。
  • すべての呼び出しasyncAspectをインターセプトする側面では、変数をクリーンアップします。これは、注釈付きメソッドのSpring処理によって要求されます。Springはプール内でメソッドを実行するため、スレッドは再利用され、破棄されません。これは、変数がスレッドに保持されることを意味します。 @asyncasyncAspectAfter()threadLocal
    @asyncthreadLocal

ThreadScope

/**
 * This scope works in conjunction with the {@link AsyncAspect} that goes to
 * cleanup the threadScoped beans after an async run. This is required since in
 * spring the async methods run in a pool of thread, so they could share some
 * thread scoped beans.
 * 
 * 
 * @author enrico.agnoli
 */
public class ThreadScope implements Scope {

    /**
     * This map contains for each bean name or ID the created object. The
     * objects are created with a spring object factory. The map is ThreadLocal,
     * so the bean are defined only in the current thread!
     */
    private final ThreadLocal<Map<String, Object>> threadLocalObjectMap = new ThreadLocal<Map<String, Object>>() {
        @Override
        protected Map<String, Object> initialValue() {
            return new HashMap<String, Object>();
        };
    };

    /** {@inheritDoc} */
    public Object get(final String beanName,
            final ObjectFactory<?> theObjectFactory) {
        Object object = threadLocalObjectMap.get().get(beanName);
        if (null == object) {
            object = theObjectFactory.getObject();
            threadLocalObjectMap.get().put(beanName, object);
        }
        return object;
    }

    /** {@inheritDoc} */
    public String getConversationId() {
        // In this case, it returns the thread name.
        return Thread.currentThread().getName();
    }

    /** {@inheritDoc} */
    public void registerDestructionCallback(final String beanName,
            final Runnable theCallback) {
        // nothing to do ... this is optional and not required
    }

    /** {@inheritDoc} */
    public Object remove(final String beanName) {
        return threadLocalObjectMap.get().remove(beanName);
    }

    @Override
    public Object resolveContextualObject(String key) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Invoke this method to cleanUp the ThreadLocal beans. This call is
     * required since in case of run in a thread pool, the thread will never be
     * removed and the threadLocal variables would be shared between two
     * different executions!
     */
    public void cleanUpThreadScopedBeans() {
        threadLocalObjectMap.remove();
    }
}

AsyncAspect

/**
 * This Async Aspect is used to cleanup the threadScoped beans after an async
 * run. This is required since in spring the async methods run in a pool of
 * thread, so they could share some thread scoped beans.<br>
 * The Thread scope is defined in {@link ThreadScope}
 * 
 * @author enrico.agnoli
 * 
 */
public class AsyncAspect {
    @Autowired
    ThreadScope threadScope;

    private static final Logger log = LoggerFactory
            .getLogger(AsyncAspect.class);

    public void asyncAspectAfter() {
        log.debug("CleanUp of the ThreadScoped beans");
        threadScope.cleanUpThreadScopedBeans();
    }

}

ApplicationContext

<!-- Here we define the Thread scope, a bean exists only inside the same thread -->
<bean id="ThreadScope" class="com.myCompany.myApp.ThreadScope" />
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
        <map>
            <entry key="thread">
                <ref bean="ThreadScope"/>
            </entry>
        </map>
    </property>
</bean>

<!-- Here we configure the aspect -->
<bean id="AsyncAspect" class="com.myCompany.myApp.AsyncAspect" />
<aop:config proxy-target-class="true">
    <aop:aspect ref="AsyncAspect">
        <aop:pointcut expression="@annotation(org.springframework.scheduling.annotation.Async)" id="asyncAspectId" />
        <aop:after pointcut-ref="asyncAspectId" method="asyncAspectAfter" />
    </aop:aspect>
</aop:config>
于 2012-10-24T14:42:03.913 に答える