Spring request scope in async service? Deploy ThreadScope on top of the threadLocal variable as well as AsyncAspect to clean

I have a web service that launches a series of actions. All these actions, starting with the same request, consist in sharing an actionContext, which contains some locks and some other information.

So far, this actionContext has been introduced by Spring in all actions using the Request scope.

Now I am implementing a web socket service to be able to follow the evolution of these actions.
Now the WebService will need to create a thread that processes the execution of the actions and returns the webSocket address to the calling application / user.

The action was implemented using the @async Spring annotation and will be run in the thread pool, as defined in the application context.

PROBLEM:
With this new functionality, the request area no longer works, since the spawned thread is not a request (Spring blocks execution).

What is the best solution to solve this problem?

  • Implement my stream scope to handle actionContext and correctly inject it into all actions?
  • Pass actionContext everywhere manually (in my opinion this does not look very good)
  • Implement a web service that creates an instance of webSocket and request the caller to call it as the first, and then pass its link to a real web service?

Thanks for the help!

+4
source share
1 answer

I decided to keep everything as clean as possible and switch to the implementation of TreadScope!

My solution consists of:

  • ThreadScope used to enter the same bean inside an entire action that runs on a single thread.
  • an asyncAspect aspect that intercepts all @async calls, asyncAspectAfter() will clear the threadLocal variable.
    This is requested by spring processing annotated @async methods: since spring runs methods in the pool, threads are reused and not destroyed. This means that the threadLocal variable will be stored in the thread.

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

Asyncappspect

 /** * 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> 
+6
source

Source: https://habr.com/ru/post/1441838/


All Articles