Using a request scoped bean outside of an actual web request

前端 未结 5 801
我在风中等你
我在风中等你 2020-12-09 15:39

I have a web application that has a Spring Integration logic running with it in a separated thread. The problem is that at some point my Spring Integration logic tries to us

5条回答
  •  -上瘾入骨i
    2020-12-09 16:15

    For spring-boot 2.4 and spring framework 5, both of the RequestContextFilter and RequestContextListener did not work for me.

    After digging into the code, I found the DispatcherServlet will overwrite the inheritable of RequestContextHolder set by RequestContextFilter or any one else, see DispatcherServlet.processRequest and DispatcherServlet.initContextHolders.

    So the solution is quite simple, without any other components:

    @Configuration
    class whateverNameYouLike {
       @Bean
       DispatcherServlet dispatcherServlet() {
           DispatcherServlet srvl = new DispatcherServlet();
           srvl.setThreadContextInheritable(true);
           return srvl;
       }
    }
    

    But notice that the solution alone only applies to new threads created by the current request thread, not regarding to any thread pool.

    For the thread pool cases, you can depend on an extra wraper class:

    public class InheritableRequestContextTaskWrapper {
        private Map parentMDC = MDC.getCopyOfContextMap();
        private RequestAttributes parentAttrs = RequestContextHolder.currentRequestAttributes();
    
        public  Function lambda1(Function runnable) {
            return t -> {
                Map orinMDC = MDC.getCopyOfContextMap();
                if (parentMDC == null) {
                    MDC.clear();
                } else {
                    MDC.setContextMap(parentMDC);
                }
    
                RequestAttributes orinAttrs = null;
                try {
                    orinAttrs = RequestContextHolder.currentRequestAttributes();
                } catch (IllegalStateException e) {
                }
                RequestContextHolder.setRequestAttributes(parentAttrs, true);
                try {
                    return runnable.apply(t);
                } finally {
                    if (orinMDC == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(orinMDC);
                    }
                    if (orinAttrs == null) {
                        RequestContextHolder.resetRequestAttributes();
                    } else {
                        RequestContextHolder.setRequestAttributes(orinAttrs, true);
                    }
                }
            };
        }
    }
    

    And then use it like this:

    InheritableRequestContextTaskWrapper wrapper = new InheritableRequestContextTaskWrapper();
    List res = pool.submit(() -> ids.parallelStream().map(
        wrapper.lambda1((String id) -> {
            try {
               // do something and return the result string
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Error occurred in async tasks", e);
            }
        })).collect(Collectors.toList())).get();
    
    

提交回复
热议问题