2016-03-08 12 views
5

Mam aplikację działającą pod Spring Boot 1.2.3, która używa metod opisanych w @Async. Do tej pory działało poprawnie.@Aynchronizacja nie działa po uaktualnieniu do Spring Boot 1.3.3

Po uaktualnieniu do Spring Boot 1.3.3 metody oznaczone jako @Async nie są wywoływane w osobnym wątku.

Oto przykładowy program, który ilustruje problem:

App.java:

package test; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.CommandLineRunner; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.scheduling.annotation.EnableAsync; 


@Configuration 
@EnableAutoConfiguration 
@ComponentScan(basePackages = { "test" }) 
@EnableAsync 
public class App implements CommandLineRunner { 

    private static final Logger log = LoggerFactory.getLogger(App.class); 

    @Autowired 
    AsyncClass async; 

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

    public void run(String... arg0) throws Exception { 
     log.info("in run"); 
     async.start(); 
     log.info("done run"); 
    } 

} 

AsyncClass.java:

package test; 

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
import org.springframework.context.annotation.Bean; 
import org.springframework.scheduling.annotation.Async; 
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
import org.springframework.stereotype.Component; 


@Component 
public class AsyncClass { 

    private static final Logger log = LoggerFactory.getLogger(AsyncClass.class); 

    @Async("myTaskExecutor") 
    public void start() { 
     log.info("in async task"); 
     try { 
      Thread.sleep(2000); 
     } catch (InterruptedException e) { } 
     log.info("done async task"); 
    } 

    @Bean 
    public ThreadPoolTaskExecutor myTaskExecutor() { 

     ThreadPoolTaskExecutor bean = new ThreadPoolTaskExecutor(); 
     bean.setCorePoolSize(1); 
     bean.setMaxPoolSize(1); 
     bean.setQueueCapacity(10); 
     bean.setThreadPriority(1); 
     bean.setWaitForTasksToCompleteOnShutdown(true); 
     return bean; 
    } 

} 

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>dbush</groupId> 
    <artifactId>async-test</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <name>async-test</name> 

    <properties> 
     <java.version>1.8</java.version> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 
     <maven.compiler.source>${java.version}</maven.compiler.source> 
     <maven.compiler.target>${java.version}</maven.compiler.target>  
    </properties> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <!-- this is the only line that differs --> 
     <version>1.3.3.RELEASE</version> 
    </parent> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-web</artifactId> 
      <exclusions> 
       <exclusion> 
        <groupId>org.slf4j</groupId> 
        <artifactId>log4j-over-slf4j</artifactId> 
       </exclusion> 
      </exclusions> 
     </dependency> 
    </dependencies> 

</project> 

W sekcji 1.2.3 dziennik instrukcje w metodzie start pokazują je jako działające w wątku myTaskExecutor-1. W sekcji 1.3.3 te same dzienniki pokazują, że działają w wątku main.

Każdy pomysł, co może być nie tak?

Odpowiedz

2

Musisz umieścić swoją metodę fabryki fasoli w innej klasie opisanej jako @konfiguracja. Executor będzie używał w ten sposób do wykonania metody @Aynchronicznej.

@Configuration 
@EnableAsync 
public class AsyncConfig { 
    @Bean(name = "myTaskExecutor") 
    public ThreadPoolTaskExecutor myTaskExecutor() { 
     return new ThreadPoolTaskExecutor(); 
    } 
} 
2

Wstrzyknięcie na klasy konfiguracji może być wyzwaniem, nie polecam go szczególnie, jeśli ta klasa jest również rzeczywistym komponentem. IMHO twoja klasa robi za dużo. Następnie przenieś konfigurację ThreadPoolTaskExecutor tam, gdzie należy.

Zamiast autowirowania, należy utworzyć metodę @Bean, która zwraca wartość CommandLineRunner zamiast jej implementacji.

@SpringBootApplication 
@EnableAsync 
public class App { 

    private static final Logger log = LoggerFactory.getLogger(App.class); 

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

    @Bean 
    public CommandLineRunner runner(AsyncClass async) { 

     return new CommandLineRunner() { 
     public void run(String... arg0) throws Exception { 
      log.info("in run"); 
      async.start(); 
      log.info("done run"); 
     }  
     }; 

    } 

    @Bean 
    public ThreadPoolTaskExecutor taskExecutor() { 

     ThreadPoolTaskExecutor bean = new ThreadPoolTaskExecutor(); 
     bean.setCorePoolSize(1); 
     bean.setMaxPoolSize(1); 
     bean.setQueueCapacity(10); 
     bean.setThreadPriority(1); 
     bean.setWaitForTasksToCompleteOnShutdown(true); 
     return bean; 
    } 
} 

Oczywiście oczyść swój AsyncClass.

+0

Dzięki. Ta konfiguracja działałaby w prostym przykładzie z wątkiem, który inicjuje przy starcie, jednak pełny program zawiera jedno z nich, a także drugie zadanie, które jest wywoływane przez żądanie sieciowe, więc umieszczanie komponentów bean "ThreadPoolTaskExecutor" w ich osobnych osobach. Klas @ Konfiguracja była najprostszym rozwiązaniem. – dbush

Powiązane problemy