私はすべてを可能な限りクリーンに保ち、TreadScopeの実装に進むことにしました!
私のソリューションは次のように構成されています。
ThreadScope
同じスレッドで実行されているすべてのアクション内に同じBeanを注入するために使用されます。
- すべての呼び出し
asyncAspect
をインターセプトする側面では、変数をクリーンアップします。これは、注釈付きメソッドのSpring処理によって要求されます。Springはプール内でメソッドを実行するため、スレッドは再利用され、破棄されません。これは、変数がスレッドに保持されることを意味します。 @async
asyncAspectAfter()
threadLocal
@async
threadLocal
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>