org.springframework.scheduling.annotation.Async
@Async注解方法, 在子线程中异步执行
https://www.baeldung.com/spring-async
失效原因
1.没有在@SpringBootApplication启动类当中添加注解@EnableAsync注解。
2.异步方法使用注解@Async的返回值只能为void或者Future。
3.没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
解决方法:
这里具体说一下第三种情况的解决方法。
1.注解的方法必须是public方法。
2.方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的。
3.如果需要从类的内部调用,需要先获取其代理类,下面上代码
@Service
public class SpringAsyncServiceImpl {
public void entry() {
System.out.println("entry thread name # " + Thread.currentThread().getName());
SpringAsyncServiceImpl asyncService = SpringUtils.getBean(SpringAsyncServiceImpl.class);
asyncService.voidMethod();
}
@Async
public void voidMethod() {
System.out.println(" thread name # " + Thread.currentThread().getName());
}
}
本类中可以定义实现本类中调用的本类的异步多线程方法;
必须实现的两种:
public方法
手动获取spring bean
@Component("springContextUtil")
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanId) {
return (T) applicationContext.getBean(beanId);
}
public static <T> T getBean(Class<T> requiredType) {
return (T) applicationContext.getBean(requiredType);
}
/**
* Spring容器启动后,会把 applicationContext 给自动注入进来,然后我们把 applicationContext
* 赋值到静态变量中,方便后续拿到容器对象
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
}
子线程抛出异常, 对调用线程的影响
@Service
public class SpringAsyncServiceImpl {
public void entry() throws ExecutionException, InterruptedException {
System.out.println("entry thread name # " + Thread.currentThread().getName());
SpringAsyncServiceImpl asyncService = SpringUtils.getBean(SpringAsyncServiceImpl.class);
// asyncService.voidMethod();
Future<String> aFuture = asyncService.fuyureReturn();
System.out.println("entry end");
System.out.println(aFuture.get());
}
@Async
public void voidMethod() {
System.out.println(" thread name # " + Thread.currentThread().getName());
// int i = 10 / 0;
}
@Async
public Future<String> fuyureReturn() {
FutureTask<String> futureTask = new FutureTask<>(() -> {
Thread.sleep(1000L);
// int i = 10 / 0;
return Thread.currentThread().getName() + " # 我沉睡了1000ms 现在我醒来了";
});
futureTask.run();
return futureTask;
}
}
子线程抛出异常, 当异步方法返回void,则异常不会被传播到调用线程。
子线程抛出异常, 当异步线程返回Future对象, 且在调用线程中执行future.get(), 则调用线程也会抛出异常。
image.png