2017-02-01 4 views
4

私は完全にアノテーション駆動のSpring Boot 1.3.5アプリを持っています。これは別のサービスBeanをオートワイヤリングする必要があるこの非同期サービスを持っています(そして将来はリポジトリをautowireする必要がありますいくつかのビジネス・ロジックを実行するために豆が、私はまだありませんよ):Spring @Asyncは自立豆の使用を許可していません

@Service 
public class AsyncService { 

    @Autowired 
    public HelpingService helpingService; 

    @Async 
    public Future<String> doFoo(String someArgument) 
      throws InterruptedException { 
     Thread.sleep(3000); 
     System.out.println("about to do Foo "+someArgument); 
     String result = ""; 

     try { 
      result = helpingService.getSomeStuff(someArgument); 
     } 
     catch (Exception e) { 
      e.printStackTrace(); 
     } 
     return new AsyncResult<String>(hello); 
    } 
} 

上記の方法は、予想通り、他のエンドポイント(非非同期)その仕事を持っている@Controller豆、から呼び出されていることこれを使用しても

@Controller 
public class MyController extends BaseController { 

    @Autowired 
    HelpingService helpingService; 
    @Autowired 
    AsyncService asyncService; 

    @RequestMapping(method=RequestMethod.GET, value={"/rest/threads/getIp/{jobId}"}, produces={"application/json"}) 
    public ResponseEntity<?> getLog(@PathVariable("jobId") String jobId) throws InterruptedException { 
     asyncService.doFoo(jobId); 
     return new ResponseEntity<>(HttpStatus.OK); 
    } 
} 

ここにはhelpingServiceの実装(それはインターフェイスです)、私は上記の@Asyncメソッドからそれをやっていないよ時に任意のメソッドを呼び出すと、完全に正常に動作します:

@Service 
@Validated 
public class HelpingServiceImpl implements HelpingService { 

    @Autowired 
    HttpSession httpSession; 

    @Value(value="${projName}") 
    private String projName; 

    public String getServerAddress(){ 
     AuthRegion region = (AuthRegion) httpSession.getAttribute("region"); 
     if (region != null) 
      return region.getServerAddress(); 
     else 
      return null; 
    } 


    @Override 
    public String getSomeStuff(String jobId) { 
     String responseString = ""; 

     String projName = this.projName; 
     String serverAddress = getServerAddress(); // Code stops here with an exception 

     // Some code here that works fine outside this thread 

     return responseString; 
    } 
} 

これがキャッチされた例外です:

about to do Foo (267) 
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.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:309) 
    at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:64) 
    at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:366) 
    at org.springframework.web.context.support.WebApplicationContextUtils$SessionObjectFactory.getObject(WebApplicationContextUtils.java:361) 
    at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:307) 
    at com.sun.proxy.$Proxy96.getAttribute(Unknown Source) 
    at corp.fernandopcg.myapp.service.ThreadServiceImpl.getRundeckServerPort(ThreadServiceImpl.java:45) 
    at corp.fernandopcg.myapp.service.ThreadServiceImpl.getJobExecutionOutput(ThreadServiceImpl.java:65) 
    at corp.fernandopcg.myapp.service.AsyncService.doFoo(AsyncService.java:40) 
    at corp.fernandopcg.myapp.service.AsyncService$$FastClassBySpringCGLIB$$7e164220.invoke(<generated>) 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) 
    at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:115) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at java.lang.Thread.run(Thread.java:745) 

私は(を同時に延長することができなかったのでいくつかの変更を加えましたが、SpringBootServletInitializerの例外をキャッチしなければなりませんでした)taskExecutor私のアプリケーションのメインクラスには、this tutorial私の意見では、私が必要とするものに

@SpringBootApplication 
@EnableAsync 
@EnableJpaRepositories(repositoryFactoryBeanClass = DataTablesRepositoryFactoryBean.class) 
public class MyApplication extends SpringBootServletInitializer implements AsyncConfigurer{ 

    public static void main(String[] args) { 
     SpringApplication.run(MyApplication.class, args); 
    } 

    @Override 
    public Executor getAsyncExecutor() { 
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
     executor.setCorePoolSize(2); 
     executor.setMaxPoolSize(2); 
     executor.setQueueCapacity(500); 
     executor.setThreadNamePrefix("SomeRandomLookup-"); 
     executor.initialize(); 
     return executor; 
    } 

    @Override 
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
     // TODO Auto-generated method stub 
     return null; 
    } 

} 

私の@Asyncサービスに、アプリケーションの他のサービスを利用できるように通知することはできますか?それが不可能な場合、私は実際にこれらのスレッド機構の使用を見ることはできません。

+1

さて、HttpSessionをサーブレットスレッドの外側で使用/注入することはできません(これはAsyncを使用して行います)。最も簡単な解決策は、コントローラ内のすべての必要な情報を抽出し、Asyncサービスにデータのみを渡すことです。 –

+0

@MonteCristoええ、これはまさに理由でした。私はスタックトレースを誤解し、 'HttpSession'について本当に考えなかった。 – fernandopcg

答えて

5

これは、リクエストスコープの注入が問題になる理由を示す大きな図です。 HelpingServiceImplは、フィールドに似ていますが、実際には常に、現在のリクエスト(スレッドローカル変数を使用して)を参照するために、各呼び出しでSpringによって解決されるプロキシである、特定の要求HttpSessionに隠された依存関係を持っています。

問題は、@Asyncを呼び出すと、それを引き起こした要求からHelpingServiceImpl呼び出しが分離されているため、同じスレッドに暗黙的に接続されていないことになります。グローバルな文脈。

HelpingServiceImplが直接HttpSessionから離れた領域を取得するのではなく、メソッドパラメータとして領域を渡すのではなく、依存関係を明示的にすることです。

+0

ありがとう!私はHttpSessionがここで問題になったとは思わなかった、私は問題が一般的にオートワイヤリングであると仮定した。メインスレッドからの標準パラメータとしてHttpSessionからの変数を受け取るようにコードを変更します。 – fernandopcg

+1

@fernandopcgヒントには、getAttribute'のプロキシコールを持つスタックトレースの行があります。このようなことが見られる場合は、どこかで魔法が発生しているというサイン(自動生成されたプロキシクラス)、使用している他の機能と魔法の悪影響がないことを確認してください。 – chrylis

+0

は、 '実際のWebリクエストの外でリクエスト属性 'を見たときに何か疑わしいはずです。私は今から春の魔法で世話をします:D – fernandopcg

関連する問題