Spring 的异步任务执行机制

1. 基本概念

Spring 的异步任务执行机制允许应用程序中的方法在单独的线程中执行,而不是在调用线程中同步执行。这样可以提高应用程序的响应性能,特别是在处理一些耗时的操作时,如网络请求、文件读写等。

2. 启用异步支持

要在 Spring 项目中使用异步任务执行机制,需要在配置类上添加 @EnableAsync 注解。如果是 Spring Boot 项目,可以在主应用类上添加该注解。

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfig {
    // 可以在这里进行更多的异步配置
}

3. 定义异步方法

在需要异步执行的方法上添加 @Async 注解。通常,这些方法应该返回 void 或者 CompletableFuture

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async
    public void asyncMethodWithVoidReturnType() {
        System.out.println("异步方法执行,当前线程: " + Thread.currentThread().getName());
    }

    @Async
    public CompletableFuture<String> asyncMethodWithReturnType() {
        System.out.println("异步方法执行,当前线程: " + Thread.currentThread().getName());
        try {
            Thread.sleep(5000); // 模拟耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("异步方法执行完成");
    }
}

4. 调用异步方法

在需要调用异步方法的地方,直接调用即可。Spring 会自动将方法的执行放到异步线程中。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutionException;

@Component
public class AsyncTaskRunner implements CommandLineRunner {

    @Autowired
    private AsyncService asyncService;

    @Override
    public void run(String... args) throws Exception {
        // 调用无返回值的异步方法
        asyncService.asyncMethodWithVoidReturnType();

        // 调用有返回值的异步方法
        CompletableFuture<String> future = asyncService.asyncMethodWithReturnType();
        try {
            String result = future.get();
            System.out.println("异步方法返回结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

处理异步任务的异常

1. 对于返回 void 的异步方法

对于返回 void 的异步方法,Spring 提供了 AsyncUncaughtExceptionHandler 接口来处理异常。需要创建一个实现该接口的类,并在配置类中进行配置。

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
        System.out.println("异步方法 " + method.getName() + " 抛出异常: " + throwable.getMessage());
        // 可以在这里进行更多的异常处理逻辑,如记录日志等
    }
}

在配置类中配置异常处理器:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return Executors.newFixedThreadPool(10);
    }

    @Bean
    public CustomAsyncExceptionHandler asyncExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return asyncExceptionHandler();
    }
}

2. 对于返回 CompletableFuture 的异步方法

对于返回 CompletableFuture 的异步方法,可以使用 CompletableFutureexceptionally 方法来处理异常。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async
    public CompletableFuture<String> asyncMethodWithReturnType() {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("异步方法执行,当前线程: " + Thread.currentThread().getName());
            try {
                // 模拟异常
                throw new RuntimeException("异步方法抛出异常");
            } catch (Exception e) {
                // 抛出异常
                throw new RuntimeException(e);
            }
        }).exceptionally(ex -> {
            System.out.println("异步方法处理异常: " + ex.getMessage());
            return "处理异常后的结果";
        });
    }
}

下面是完整的代码示例,以 doubaocanvas 的形式展示:

AsyncConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return Executors.newFixedThreadPool(10);
    }

    @Bean
    public CustomAsyncExceptionHandler asyncExceptionHandler() {
        return new CustomAsyncExceptionHandler();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return asyncExceptionHandler();
    }
}    

AsyncService.java

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;

@Service
public class AsyncService {

    @Async
    public void asyncMethodWithVoidReturnType() {
        System.out.println("异步方法执行,当前线程: " + Thread.currentThread().getName());
    }

    @Async
    public CompletableFuture<String> asyncMethodWithReturnType() {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("异步方法执行,当前线程: " + Thread.currentThread().getName());
            try {
                // 模拟异常
                throw new RuntimeException("异步方法抛出异常");
            } catch (Exception e) {
                // 抛出异常
                throw new RuntimeException(e);
            }
        }).exceptionally(ex -> {
            System.out.println("异步方法处理异常: " + ex.getMessage());
            return "处理异常后的结果";
        });
    }
}    

AsyncTaskRunner.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutionException;

@Component
public class AsyncTaskRunner implements CommandLineRunner {

    @Autowired
    private AsyncService asyncService;

    @Override
    public void run(String... args) throws Exception {
        // 调用无返回值的异步方法
        asyncService.asyncMethodWithVoidReturnType();

        // 调用有返回值的异步方法
        CompletableFuture<String> future = asyncService.asyncMethodWithReturnType();
        try {
            String result = future.get();
            System.out.println("异步方法返回结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}    

CustomAsyncExceptionHandler.java

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
        System.out.println("异步方法 " + method.getName() + " 抛出异常: " + throwable.getMessage());
        // 可以在这里进行更多的异常处理逻辑,如记录日志等
    }
}    

通过以上步骤,你可以在 Spring 项目中使用异步任务执行机制,并有效地处理异步任务中可能出现的异常。