我决定尽可能保持清洁并实施 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>