目录
1.配置异步线程池Bean 以及 异常处理器
2.编写需要异步执行的业务代码
3.实现一个异步任务的管理器,并使用AOP增强业务代码,主要是为了监控异步任务的执行状态
注意:Spring的异步任务,如果出现异常,不会被@ControllerAdvise和@ExcpeitonHandler捕获
package com.imooc.ecommerce.config;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
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 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@EnableAsync//开启Spring异步任务支持,放到启动类上也可以
@Configuration
public class AsyncConfig implements AsyncConfigurer {
public static final String ASYNC_EXECUTOR_NAME = "asyncExecutor";
//把我们自定义的线程池注入到IOC容器中,这个线程池会用来执行我们的异步任务
@Override
@Bean
public Executor asyncExecutor() {
//这个是Spring提供的线程池,当然我们也可以用Java库提供的
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(10);
//最大线程数
executor.setMaxPoolSize(20);
//阻塞队列大小
executor.setQueueCapacity(20);
//线程存活时间(应该是超过核心线程数的线程的存活时间)
executor.setKeepAliveSeconds(60);
//自定义线程名字前缀
executor.setThreadNamePrefix("AsyncThread-");
//关闭线程池时会等待所有任务执行完毕后在关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
//在关闭线程池时,如果一个任务的执行时长超过了60秒仍未执行完(有可能是阻塞),会强制关闭
executor.setAwaitTerminationSeconds(60);
//定义拒绝策略
//CallerRunsPolicy 如果阻塞队列满了,直接在提交该任务的线程中运行该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池,初始化core线程
executor.initialize();
return executor;
}
//指定系统中的异步任务在出现异常时用到的处理器
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
//自定义异步任务异常处理器
@SuppressWarnings("all")
class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
//三个参数依次是异常,抛出异常的方法和方法的参数
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
throwable.printStackTrace();
log.error("Async Error:[{}], Method:[{}], Param:[{}]", throwable.getMessage(), method.getName(), JSON.toJSONString(objects));
}
}
}
package com.imooc.ecommerce.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class AsyncTestServiceImpl implements IAsyncTestService{
@Override
@Async(AsyncConfig.ASYNC_EXECUTOR_NAME)//此方法异步执行,括号内参数表明使用的线程池
public void asyncTest(String taskId) {
System.out.println("开始执行异步任务" + taskId);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在执行业务代码.......");
//模拟异常
/*if (1 == 1) {
throw new RuntimeException("模拟异常!!!");
}*/
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("异步任务执行结束" + taskId);
}
}
管理器:
package com.imooc.ecommerce.service;
import com.imooc.ecommerce.constant.AsyncTaskStatusEnum;
import com.imooc.ecommerce.vo.AsyncTaskInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 异步任务执行管理器
* 对异步任务进行包装管理,记录并塞入异步任务执行信息
*/
@Slf4j
@Component
public class AsyncTaskManager {
/**
* 异步任务执行信息容器
*/
private final Map taskContainer = new HashMap<>(16);
@Resource
private IAsyncTestService asyncTestService;
//初始化异步任务
public AsyncTaskInfo initTask() {
AsyncTaskInfo taskInfo = new AsyncTaskInfo();
//设置一个唯一的异步任务id, 只要唯一即可
taskInfo.setTaskId(UUID.randomUUID().toString());
taskInfo.setStatus(AsyncTaskStatusEnum.STARTED);
taskInfo.setStartTime(new Date());
taskContainer.put(taskInfo.getTaskId(), taskInfo);
return taskInfo;
}
//提交异步任务
public AsyncTaskInfo submit() {
AsyncTaskInfo taskInfo = initTask();
asyncTestService.asyncTest(taskInfo.getTaskId());
return taskInfo;
}
//设置异步任务执行状态信息
public void setTaskInfo(AsyncTaskInfo asyncTaskInfo) {
taskContainer.put(asyncTaskInfo.getTaskId(), asyncTaskInfo);
}
//获取异步任务执行状态信息
public AsyncTaskInfo getTaskInfo(String taskId) {
return taskContainer.get(taskId);
}
}
AOP:
package com.imooc.ecommerce.monitor;
import com.imooc.ecommerce.constant.AsyncTaskStatusEnum;
import com.imooc.ecommerce.service.AsyncTaskManager;
import com.imooc.ecommerce.vo.AsyncTaskInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
@Slf4j
@Aspect
@Component
public class AsyncTaskMonitor {
@Resource
private AsyncTaskManager asyncTaskManager;
@Around("execution(* com.imooc.ecommerce.service.AsyncTestServiceImpl.*(..))")
public Object taskHandle(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取taskId
String taskId = proceedingJoinPoint.getArgs()[0].toString();
//获取任务信息,在提交任务时就已经放入到容器中了
AsyncTaskInfo taskInfo = asyncTaskManager.getTaskInfo(taskId);
log.info("AsyncTaskMonitor is monitoring async task:[{}]", taskId);
//设置为运行状态,并重新放入容器
taskInfo.setStatus(AsyncTaskStatusEnum.RUNNING);
asyncTaskManager.setTaskInfo(taskInfo);
AsyncTaskStatusEnum status;
Object result;
try {
result = proceedingJoinPoint.proceed();
status = AsyncTaskStatusEnum.SUCCESS;
} catch (Throwable throwable) {
throwable.printStackTrace();
log.error("异常出现了!!!");
result = null;
status =AsyncTaskStatusEnum.FAILED;
asyncTaskManager.setTaskInfo(taskInfo);
throw throwable;
}
//设置异步任务其他的信息,再次重新放入到容器中
taskInfo.setEndTime(new Date());
taskInfo.setStatus(status);
taskInfo.setTotalTime(String.valueOf(taskInfo.getEndTime().getTime() - taskInfo.getStartTime().getTime()));
asyncTaskManager.setTaskInfo(taskInfo);
return result;
}
}
最后,在controller调用管理器中的submit方法即可执行异步任务。调用getTaskInfo方法可随时查看异步任务的执行状态。
@GetMapping("/test")
public AsyncTaskInfo test() {
return asyncTaskManager.submit();
}
@GetMapping("/task-info")
public AsyncTaskInfo getTaskInfo(@RequestParam String taskId) {
return asyncTaskManager.getTaskInfo(taskId);
}
entity:
package com.imooc.ecommerce.vo;
import com.imooc.ecommerce.constant.AsyncTaskStatusEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AsyncTaskInfo {
private String taskId;
//执行状态
private AsyncTaskStatusEnum status;
private Date startTime;
private Date endTime;
private String totalTime;
}
package com.imooc.ecommerce.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 异步任务状态枚举
*/
@Getter
@AllArgsConstructor
public enum AsyncTaskStatusEnum {
STARTED(0, "已经启动"),
RUNNING(1, "正在运行"),
SUCCESS(2, "执行成功"),
FAILED(3, "执行失败");
//执行状态码
private final int state;
//执行状态描述
private final String stateInfo;
}