2017-02-09 77 views
1

我有一個注入某些bean的spring應用程序,它基於請求上下文注入。在這個例子中,它是Facebook bean。Spring請求lambda中的作用域bean

@RestController 
@RequestMapping("facebook") 
public class FacebookInjectionController { 

    @Autowired 
    private Facebook facebook; 

    @Autowired 
    private UserRepository userRepository; 

    @RequestMapping(method = RequestMethod.GET) 
    public List<String> blah() { 
     String firstName = facebook.userOperations().getUserProfile().getFirstName(); 
     return Arrays.asList(firstName); 
    } 

    @RequestMapping(method = RequestMethod.GET, value = "complex") 
    public List<String> blah2() { 
     UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 

     Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true); 

     return stream.filter(u -> u.getUid().equals(principal.getUid())) 
       .map(u -> 
         facebook.userOperations().getUserProfile().getFirstName() 
       ).collect(Collectors.toList()); 
    } 

} 

該代碼將正常運行,但每隔一段時間就會失敗,出現以下錯誤:

2017-02-09 01:39:59.133 ERROR 40802 --- [o-auto-1-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.facebook': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.] with root cause

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request. 
    at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131) 
    at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:41) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187) 
    at com.sun.proxy.$Proxy137.userOperations(Unknown Source) 
    at com.roomsync.FacebookInjectionController.lambda$blah2$5(FacebookInjectionController.java:43) 
    at com.roomsync.FacebookInjectionController$$Lambda$10/2024009478.apply(Unknown Source) 
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175) 
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) 
    at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747) 
    at java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721) 
    at java.util.stream.AbstractTask.compute(AbstractTask.java:316) 
    at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731) 
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) 
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:902) 
    at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1689) 
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1644) 
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) 

我已經嘗試了多種解決方案(包括Spring MVC: How to use a request-scoped bean inside a spawned thread?)但都沒有奏效。

有沒有辦法通過一個請求範圍豆到lambda和另一個線程?

要去什麼https://stackoverflow.com/users/1262865/john16384說我改變了我的config來:

@Bean 
@Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES) 
public ConnectionRepository connectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { 
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
    if (authentication == null) { 
     throw new IllegalStateException("Unable to get a ConnectionRepository: no user signed in"); 
    } 
    return getUsersConnectionRepository(connectionFactoryLocator).createConnectionRepository(authentication.getName()); 
} 

@Bean 
@Scope(value="inheritableThreadScope", proxyMode=ScopedProxyMode.INTERFACES) 
public Facebook facebook(ConnectionFactoryLocator connectionFactoryLocator) { 
    Connection<Facebook> connection = connectionRepository(connectionFactoryLocator).findPrimaryConnection(Facebook.class); 

    return connection != null ? connection.getApi() : null; 
} 

@Bean 
@Scope(value = "inheritableThreadScope", proxyMode = ScopedProxyMode.INTERFACES) 
public ExecutorService fbExecutor() { 
    return Executors.newSingleThreadExecutor(); 
} 

控制器現在看起來像:

@RestController 
@RequestMapping("facebook") 
public class FacebookInjectionController { 

    @Autowired 
    private Facebook facebook; 

    @Autowired 
    private UserRepository userRepository; 

    @Autowired 
    private ExecutorService fbExecutor; 

    @RequestMapping(method = RequestMethod.GET) 
    public List<String> blah() { 
     String firstName = facebook.userOperations().getUserProfile().getFirstName(); 
     return Arrays.asList(firstName); 
    } 

    @RequestMapping(method = RequestMethod.GET, value = "complex") 
    public List<String> blah2() throws ExecutionException, InterruptedException { 
     UserJwt principal = (UserJwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 

     Stream<User> stream = StreamSupport.stream(userRepository.findAll().spliterator(), true); 

     Future<List<String>> submit = fbExecutor.submit(() -> stream.filter(u -> u.getUid().equals(principal.getUid())) 
       .map(u -> 
         facebook.userOperations().getUserProfile().getFirstName() 
       ) 
       .collect(Collectors.toList())); 

     return submit.get(); 
    } 

} 

我也有以下配置:

@Configuration 
public class BeanFactoryConfig implements BeanFactoryAware { 
    private static final Logger LOGGER = Logger.getLogger(BeanFactoryConfig.class); 

    @Override 
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 
     if (beanFactory instanceof ConfigurableBeanFactory) { 

//   logger.info("MainConfig is backed by a ConfigurableBeanFactory"); 
      ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory; 

      /*Notice: 
      *org.springframework.beans.factory.config.Scope 
      * != 
      *org.springframework.context.annotation.Scope 
      */ 
      org.springframework.beans.factory.config.Scope simpleThreadScope = new SimpleThreadScope() { 
       @Override 
       public void registerDestructionCallback(String name, Runnable callback) { 
             RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); 
        attributes.registerDestructionCallback(name, callback, 3); 
       } 
      }; 
      cbf.registerScope("inheritableThreadScope", simpleThreadScope); 

      /*why the following? Because "Spring Social" gets the HTTP request's username from 
      *SecurityContextHolder.getContext().getAuthentication() ... and this 
      *by default only has a ThreadLocal strategy... 
      *also see https://stackoverflow.com/a/3468965/923560 
      */ 
      SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); 

     } 
     else { 
//   logger.info("MainConfig is not backed by a ConfigurableBeanFactory"); 
     } 
    } 
} 

即使有這個,它有時會得到錯誤:

{ 
    "timestamp": 1486686875535, 
    "status": 500, 
    "error": "Internal Server Error", 
    "exception": "java.util.concurrent.ExecutionException", 
    "message": "org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.facebook' defined in class path resource [com/roomsync/config/SocialConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.social.facebook.api.Facebook]: Factory method 'facebook' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.connectionRepository': Scope 'inheritableThreadScope' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.", 
    "path": "/facebook/complex" 
} 

如此看來,即時通訊仍然缺少一塊激活範圍和複製線程局部上下文它

+0

添加您的web.xml請 – Akshay

+0

我想改變TRUE;在調用'FALSE'創建流是不是一種選擇?您可能會在流操作之前緩存不依賴於「u」的值。 –

回答

0

它是必需,該流並行處理?這會導致lambda可能在另一個線程中執行。

Stream stream = StreamSupport.stream(userRepository.findAll().spliterator(), false);

+0

我將其設置爲true,因爲這是模仿另一個將在另一個線程中執行lambda的控制器中的行爲。 – arhimmel

0

有兩件事情正在進行:

1)Java流使用共同的叉/加入池並行執行的事情。這些線程不是由Spring框架(或由你)創建的。

2)請求範圍豆通過使用一個ThreadLocal支撐。

這意味着如果一個不是由Spring創建的線程試圖訪問一個請求範圍的bean,它將不會被找到,因爲線程不知道它(它不在ThreadLocal中)。

爲了讓您能夠解決這個問題,你將需要採取哪些線程用於您的流控制。一旦你完成了這個任務,你可以創建一個請求範圍bean的副本,用於子線程。在線程完成其任務後,您還需要再次清理它們,否則可能會在該線程上執行的下一個任務看到後面留下的bean。

要更改線程由並行數據流使用,請參閱:Custom thread pool in Java 8 parallel stream

如何配置春季正確傳播請求範圍豆你已經找到我認爲子線程。

+0

我更新了我的問題,嘗試了您的建議並仍然遇到問題 – arhimmel

相關問題