5

我有一个启动一系列操作的网络服务。所有这些从同一个请求开始的动作都共享一个包含一些锁和一些其他信息的actionContext 。

到目前为止,这个actionContext对象是由 Spring 在所有使用“请求”范围的操作中注入的。

现在我正在实现一个 Web 套接字服务,以便能够跟踪这些操作的演变。
webService 现在必须生成一个线程来处理操作执行并将 webSocket 地址返回给调用应用程序/用户。

该操作已使用 spring 的 @async 注释实现,并将在应用程序上下文中定义的线程池中运行。

问题:
有了这个新功能,“请求”范围不再起作用,因为产生的线程不是请求(Spring 将阻止执行)。

处理这个问题的最佳解决方案是什么?

  • 实现我的Thread 范围来处理actionContext并在所有操作中正确注入它?
  • 手动将actionContext传递到任何地方(在我看来它看起来不太好)
  • 实现一个实例化 webSocket 的 webService 并请求调用者首先调用它,然后将其引用传递给真正的 webService?

谢谢您的帮助!

4

1 回答 1

6

我决定尽可能保持清洁并实施 TreadScope!

我的解决方案由以下组成:

  • 用于在ThreadScope同一线程中运行的所有操作中注入相同的 bean。
  • asyncAspect拦截所有@async调用的方面,asyncAspectAfter()将去清理threadLocal变量。
    这是 Spring 处理带@async注释的方法所要求的:由于 Spring 在池中运行方法,因此线程被重用而不被破坏。这意味着该threadLocal变量将持续存在于线程中。

线程范围

/**
 * 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();
    }
}

异步方面

/**
 * 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();
    }

}

应用程序上下文

<!-- 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 回答