2017-02-09 18 views
1

私は、特定のBeanを注入するSpringアプリケーションをリクエストコンテキストに基づいて挿入しています。この例ではFacebook Beanです。私は(Spring MVC: How to use a request-scoped bean inside a spawned thread?含む)複数の解決策を試してみましたが、どれも働いていない春ラムダスコープのBeanをリクエスト

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) 

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

} 

このコードは正常に動作しますが、しょっちゅうそれが次のエラーで失敗します。

要求スコープBeanをラムダまたは別のスレッドに渡す方法はありますか?私はまた、以下の設定を持っている

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

} 

:コントローラは、今のように見える

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

@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"); 
     } 
    } 
} 

https://stackoverflow.com/users/1262865/john16384は、私は私の設定を変更して言ったのだろう

これでもエラーが発生することがあります:

{ 
    "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

にスレッドローカルコンテキストをコピーするようですか?そのため、ラムダは別のスレッドで実行される可能性があります。

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

+0

これは、別のスレッドでラムダを実行する別のコントローラでの振る舞いを模倣しているため、実際にはtrueに設定しました。 – arhimmel

0

があります起こって二つのこと:

1)Javaは、並行して物事を実行するために、プールに参加/共通フォークを使用してストリーム。これらのスレッドは、Springフレームワーク(またはあなた自身)によって作成されません。

2)リクエストスコープBeanは、ThreadLocalを使用してサポートされています。

これは、Springで作成されていないスレッドがリクエストスコープのBeanにアクセスしようとすると、スレッドがそれを知らないため(ThreadLocalにない)スレッドが見つからないことを意味します。

この問題を解決するには、ストリームに使用されているスレッドを制御する必要があります。これを達成したら、リクエストスコープのBeanのコピーをサブスレッドに使用することができます。また、スレッドがタスクを完了した後にそれらをもう一度クリーンアップする必要があります。または、そのスレッドで実行されている次のタスクで見られる豆を残す危険性があります。参照、スレッドを並列ストリームで使用される変更するには

Custom thread pool in Java 8 parallel stream

要求を伝播するために、適切に春を構成する方法は、すでに私が考えていた子スレッドに豆をスコープ。

+0

iveさんがあなたの提案で私の試みを更新しても問題は解決しました – arhimmel

関連する問題