控制器中的Spring Boot @Async方法同步执行

时间:2015-03-26 16:35:22

标签: java ajax spring-mvc asynchronous spring-boot

我的[基本] Spring Boot应用程序接受来自浏览器的请求,通过jQuery.get()发送,并且应该立即收到响应 - 例如“您的请求已排队”。为此,我写了一个控制器:

@Controller
public class DoSomeWorkController {

  @Autowired
  private final DoWorkService workService;

  @RequestMapping("/doSomeWork")
  @ResponseBody
  public String doSomeWork() {

    workService.doWork(); // time consuming operation
    return "Your request has been queued.";
  }
}

DoWorkServiceImpl类实现了DoWorkService接口,非常简单。它有一种方法来执行耗时的任务。我不需要从此服务调用返回任何内容,因为电子邮件将在工作结束时发送,包括失败或成功方案。所以它实际上看起来像:

@Service
public class DoWorkServiceImpl implements DoWorkService {

  @Async("workExecutor")
  @Override
  public void doWork() {

    try {
        Thread.sleep(10 * 1000);
        System.out.println("completed work, sent email");
    }
    catch (InterruptedException ie) {
        System.err.println(ie.getMessage());
    }
  }
}

我认为这样可行,但浏览器的Ajax请求在返回响应之前等待了10秒。因此,控制器映射方法正在调用同步注释@Async的内部方法,看起来如此。在传统的Spring应用程序中,我通常将其添加到XML配置中:

<task:annotation-driven />
<task:executor id="workExecutor" pool-size="1" queue-capacity="0" rejection-policy="DISCARD" />

所以我认为在主应用程序类中编写相同的内容会有所帮助:

@SpringBootApplication
@EnableAsync
public class Application {

  @Value("${pool.size:1}")
  private int poolSize;;

  @Value("${queue.capacity:0}")
  private int queueCapacity;

  @Bean(name="workExecutor")
  public TaskExecutor taskExecutor() {
      ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
      taskExecutor.setMaxPoolSize(poolSize);
      taskExecutor.setQueueCapacity(queueCapacity);
      taskExecutor.afterPropertiesSet();
      return taskExecutor;
  }

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

这并未改变行为。发送请求10秒后,Ajax响应仍然到达。我错过了什么?

Spring Boot应用程序可以是downloaded here。安装Maven后,可以使用简单的命令运行项目:

mvn clean spring-boot:run

注意由于@Dave Syer在下面提供了答案,问题得到了解决,他指出我的应用程序中缺少@EnableAsync,即使我在上面的代码片段。

5 个答案:

答案 0 :(得分:41)

您正在从同一个类中的另一个方法调用@Async方法。除非您为@EnableAsync启用AspectJ代理模式(并提供当然的编织器),否则无法工作(google&#34;代理自我调用&#34;)。最简单的解决方法是将@Async方法放在另一个@Bean

答案 1 :(得分:3)

我有一个类似的问题,我在正确的bean中有@Async和@EnableAsync注释,但仍然是同步执行的方法。在检查了日志后,有一个警告说我有多个ThreadPoolTask​​Executor类型的bean,并且没有一个叫做 taskExecutor 所以......

@Bean(name="taskExecutor")
public ThreadPoolTaskExecutor defaultTaskExecutor() {
     ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
     //Thread pool configuration
     //...
     return pool;
}

有关线程池可用的配置,请参阅http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.html

答案 2 :(得分:1)

作为@ dave-syer答案的代码示例:

这异步工作:

var arrayIn = [
      {
        vendor_uLID: '5e793a0411d2bef2e375cd00',
        productVariations: [
          {
            variationName: 'Colour',
            variationOptions: [
              {
                name: 'Blue',
                record_uLID: '6afa239e-ce53-40eb-addc-836d8ecc0051',
              },
              {
                name: 'green',
                record_uLID: '66654830-6850-490a-8eaf-9d505e3e4672',
              },
            ],
          },
          {
            variationName: 'Pattern',
            variationOptions: [
              {
                name: 'Bold',
                record_uLID: '6afa239e-ce53-40eb-addc-836d8ecc0055',
              },
              {
                name: 'Spotted',
                record_uLID: '66654830-6850-490a-8eaf-9d505e3e4671',
              }
            ],
          },
        ],
      },
      {
        vendor_uLID: '5e7bb266071f9601b6ad8f4e',
        productVariations: [
          {
            variationName: 'Colour',
            variationOptions: [
              {
                name: 'Blue',
                record_uLID: '6afa239e-ce53-40eb-addc-836d8ecc0051',
              },
              {
                name: 'purple',
                record_uLID: '66654830-6850-490a-8eaf-9d505e3e4672',
              },
            ],
          },
          {
            variationName: 'Pattern',
            variationOptions: [
              {
                name: 'Bold',
                record_uLID: '6afa239e-ce53-40eb-addc-836d8ecc0055',
              },
              {
                name: 'Spotted',
                record_uLID: '66654830-6850-490a-8eaf-9d505e3e4671',
              }
            ],
          },
        ],
      }
    ]

但这不是:

  [
      {
        "variationName": "Colour",
        "variationOptions": [
          {
            "name": "Blue",
            "record_uLID": "6afa239e-ce53-40eb-addc-836d8ecc0051",
            "linkedVendors": [
              "5e793a0411d2bef2e375cd00",
              "5e7bb266071f9601b6ad8f4e",
            ]
          },
          {
            "name": "green",
            "record_uLID": "66654830-6850-490a-8eaf-9d505e3e4672",
            "linkedVendors": [
              "5e793a0411d2bef2e375cd00",
            ]
          },
          {
            "name": "purple",
            "record_uLID": "66654830-6850-490a-8eaf-9d505e3e4675",
            "linkedVendors": [
              "5e7bb266071f9601b6ad8f4e",
            ]
          }
        ]
      },
      {
        "variationName": "Pattern",
        "variationOptions": [
          {
            "name": "Bold",
            "record_uLID": "6afa239e-ce53-40eb-addc-836d8ecc0055",
            "linkedVendors": [
              "5e7bb266071f9601b6ad8f4e",
              "5e793a0411d2bef2e375cd00"
            ]
          },
          {
            "name": "Spotted",
            "record_uLID": "66654830-6850-490a-8eaf-9d505e3e4671",
            "linkedVendors": [
              "5e793a0411d2bef2e375cd00",
              "5e7bb266071f9601b6ad8f4e"

            ]
          }
        ]
      }]

答案 3 :(得分:1)

我使用spring-boot主类定义异步配置@EnableAsync注释使Spring能够在后台线程池中运行@Async方法。此类还通过定义新bean来定制Executor。在这里,该方法名为taskExecutor(),因为这是Spring搜索的特定方法名称。

Spring-Boot-Application.class

@SpringBootApplication
@EnableAsync
public class AsyncApplication {

    @Value("${config.threadpool.corepool.size}")
    private Integer corePoolSize;

    @Value("${config.threadpool.maxpool.size}")
    private Integer maxPoolSize;

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

    //name of below method should not be changed.
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        //other proeprties to be set here
        executor.setThreadNamePrefix("ASYNC-");
        executor.initialize();
        return executor;
    }
}

在实现中,在方法级别使用@Async以使方法异步。必须公开使用​​@Async的方法。另外,调用@Async方法的带有注释的方法@Async无效。

示例实现,以供参考-

@Async
  public void updateData(String userId) throws ApplicationException {
    logger.info("Updating details for User with {}", userId);
    //your code goes here...
  }

配置属性在application.properties文件中定义

#Core Pool Size for Async
config.threadpool.corepool.size=100
#Max Pool Size for Async
config.threadpool.maxpool.size=400   

有关如何定义池的规则,请参阅rules-of-a-threadpool-executor

答案 4 :(得分:0)

按照以下三个步骤操作:

1步骤: 将@EnableAsync与@configuration或@SpringBootApplication

一起使用

@EnableAsync public class Application {

2步骤:

/**
 * THIS FOR ASYNCRONOUS PROCESS/METHOD
 * @return
 */
@Bean
public Executor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(5);
    executor.setQueueCapacity(500);
    executor.setThreadNamePrefix("Asynchronous Process-");
    executor.initialize();
    return executor;
}

3步骤:将@Async放在预期的方法

Ť