• Spring Boot 之异步执行方法


    前言:

    最近的时候遇到一个需求,就是当服务器接到请求并不需要任务执行完成才返回结果,可以立即返回结果,让任务异步的去执行。开始考虑是直接启一个新的线程去执行任务或者把任务提交到一个线程池去执行,这两种方法都是可以的。但是Spring 这么强大,肯定有什么更简单的方法,就 google 了一下,还真有呢。就是使用 @EnableAsync 和 @Async 这两个注解就 ok 了。

    给方法加上 @Async 注解

    package me.deweixu.aysncdemo.service;
    public interface AsyncService {
    void asyncMethod(String arg);
    }
    package me.deweixu.aysncdemo.service.ipml;
    import me.deweixu.aysncdemo.service.AsyncService;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    @Service
    public class AsyncServiceImpl implements AsyncService {
    @Async
    @Override
    public void asyncMethod(String arg) {
    System.out.println("arg:" + arg);
    System.out.println("=====" + Thread.currentThread().getName() + "=========");
    }
    }

    @EnableAsync

    在启动类或者配置类加上 @EnableAsync 注解

    package me.deweixu.aysncdemo;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.scheduling.annotation.EnableAsync;
    @EnableAsync
    @SpringBootApplication
    public class AysncDemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(AysncDemoApplication.class, args);
    }
    }

    测试

    package me.deweixu.aysncdemo;
    import me.deweixu.aysncdemo.service.AsyncService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class AysncDemoApplicationTests {
    @Autowired
    AsyncService asyncService;
    @Test
    public void testAsync() {
    System.out.println("=====" + Thread.currentThread().getName() + "=========");
    asyncService.asyncMethod("Async");
    }
    }

    =====main=========
    2018-03-25 21:30:31.391 INFO 28742 --- [ main] .s.a.AnnotationAsyncExecutionInterceptor : No task executor bean found for async processing: no bean of type TaskExecutor and no bean named 'taskExecutor' either
    arg:Async
    =====SimpleAsyncTaskExecutor-1=========

    从上面的结果看 asyncService.asyncMethod("Async") 确实异步执行了,它使用了一个新的线程。

    指定 "Executor"

    从上面执行的日志可以猜测到 Spring 默认使用 SimpleAsyncTaskExecutor 来异步执行任务的,可以搜索到这个类。 @Async 也可以指定自定义的 Executor。

    在启动类中增加自定义的 Executor

    package me.deweixu.aysncdemo;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import java.util.concurrent.Executor;
    @EnableAsync
    @SpringBootApplication
    public class AysncDemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(AysncDemoApplication.class, args);
    }
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
    return new ThreadPoolTaskExecutor();
    }
    }

    指定 "Executor"

    package me.deweixu.aysncdemo.service.ipml;
    import me.deweixu.aysncdemo.service.AsyncService;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    @Service
    public class AsyncServiceImpl implements AsyncService {
    @Async("threadPoolTaskExecutor")
    @Override
    public void asyncMethod(String arg) {
    System.out.println("arg:" + arg);
    System.out.println("=====" + Thread.currentThread().getName() + "=========");
    }
    }

    这样在异步执行任务的时候就使用 threadPoolTaskExecutor

    设置默认的

    Executor

    上面提到如果 @Async 不指定 Executor 就默认使用 SimpleAsyncTaskExecutor,其实默认的 Executor 是可以使用 AsyncConfigurer 接口来配置的

    @Configuration
    public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
    return new ThreadPoolTaskExecutor();
    }

    }

    异常捕获

    在异步执行的方法中是可能出现异常的,我们可以在任务内部使用 try catch 来处理异常,当任务抛出异常时, Spring 也提供了捕获它的方法。

    实现 AsyncUncaughtExceptionHandler 接口

    public class CustomAsyncExceptionHandler
    implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(
    Throwable throwable, Method method, Object... obj) {

    System.out.println("Exception message - " + throwable.getMessage());
    System.out.println("Method name - " + method.getName());
    for (Object param : obj) {
    System.out.println("Parameter value - " + param);
    }
    }

    }

    实现 AsyncConfigurer 接口重写 getAsyncUncaughtExceptionHandler 方法
    @Configuration
    public class SpringAsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
    return new ThreadPoolTaskExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new CustomAsyncExceptionHandler();
    }

    }

    改写 asyncMethod 方法使它抛出异常

     @Async
    @Override
    public void asyncMethod(String arg) {
    System.out.println("arg:" + arg);
    System.out.println("=====" + Thread.currentThread().getName() + "=========");
    throw new NullPointerException();
    }

    运行结果:

    =====main=========
    arg:Async
    =====threadPoolTaskExecutor-1=========
    Exception message - Async NullPointerException
    Method name - asyncMethod
    Parameter value - Async

    正确捕获到了异常。

    我是小架,我们

    下篇文章见!

  • 相关阅读:
    vim使用
    .net程序员转java之mybatis学习
    Redis 学习笔记
    Effective C# 笔记01
    Android 开发感想
    入职两个月,WPF开发感想
    Oracle 学习笔记二
    Oracle 开发使用笔记一
    Java 集合——Map集合
    Java 集合——Set集合
  • 原文地址:https://www.cnblogs.com/sevencutekk/p/11559000.html
Copyright © 2020-2023  润新知