Springboot中执行异步任务,可以使用线程池,也可以直接使用@Async
注解来实现异步任务。
而@Async也不是可以随便使用的,如果使用方式不对,也可能做了无用功,@Async不生效,导致实际还是同步执行
因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,没有经过Spring容器,无法使用代理对象调用
即被调用方法 和 调用处的代码都处在同一个类,所以只是相当于本类调用,并没有使用代理类 从而@Async并没有产生效果
1、首先Application启动类必须设置@EnableAsync,否则不生效
@EnableAsync //就是这个
@SpringBootApplication(scanBasePackages = {"com.msedu"})
public class DistributionApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DistributionApplication.class);
application.setBannerMode(Banner.Mode.OFF);
ConfigurableApplicationContext run = application.run(args);
}
}
2、定义一个Controller,controller调用serviceA方法,serviceA方法调用serviceB方法(@Async)
(1)用例1
1、MyController
public class MyController {
@PostMapping("/testSync")
public RespMessage testSync() {
aService.mainFunc();
return RespHandler.success();
}
}
2、AService
@Service
public class AService {
@Override
public void mainFunc() {
logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
sync();
logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
}
@Async
public void sync() {
logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
}
}
//输出结果
2022-04-01 11:06:31.893 [TID: N/A] [http-nio-8103-exec-1] INFO c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-1,start:1648782391893
2022-04-01 11:06:31.894 [TID: N/A] [http-nio-8103-exec-1] INFO c.m.d.g.s.i.AService -异步方法-http-nio-8103-exec-1,start:1648782391894
2022-04-01 11:06:34.894 [TID: N/A] [http-nio-8103-exec-1] INFO c.m.d.g.s.i.AService -异步方法-http-nio-8103-exec-1,end:1648782394894
2022-04-01 11:06:34.894 [TID: N/A] [http-nio-8103-exec-1] INFO c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-1,end:1648782394894
2022-04-01 11:06:34.899 [TID: N/A] [http-nio-8103-exec-1] INFO c.m.d.common.config.ControllerAspect -request(c9db041be0b4429ca432e2b8678f56b3)end, cost 3340ms
//结果
主方法mainFunc和子异步方法sync,同步执行了,【@Async不生效】
【
子异步方法sync虽然加了@Async,但由于sync是在同个类里面,对于主方法mainFunc来说,调用子异步方法sync是通过当前对象去调用的,而不是通过代理对象,因而@Async不生效
】
(2)用例2
1、MyController
public class MyController {
@PostMapping("/testSync2")
public RespMessage testSync2() {
aService.mainFunc2();
return RespHandler.success();
}
}
2、AService
@Service
public class AService {
@Autowire
private BService bservice;
@Override
public void mainFunc2() {
logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
bservice.sync();
logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
}
}
3、BService
@Service
public class BService {
@Async
public void sync() {
logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
}
}
//输出结果
2022-04-01 11:07:25.608 [TID: N/A] [http-nio-8103-exec-2] INFO c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-2,start:1648782445608
2022-04-01 11:07:25.612 [TID: N/A] [http-nio-8103-exec-2] INFO c.m.d.g.s.i.AService -主方法-http-nio-8103-exec-2,end:1648782445612 //4ms
2022-04-01 11:07:25.621 [TID: N/A] [task-scheduler-1] INFO c.m.d.g.s.i.BService -异步方法-task-scheduler-1,start:1648782445621 //真实异步
2022-04-01 11:07:28.621 [TID: N/A] [task-scheduler-1] INFO c.m.d.g.s.i.BService -异步方法-task-scheduler-1,end:1648782448621
//结果
主方法Aservice.mainFunc和子异步方法Bservice.sync,异步执行了,【@Async生效】
(3)用例3
1、MyController
public class MyController {
@PostMapping("/testSync3")
public RespMessage testSync3() {
aService.mainFunc3();
return RespHandler.success();
}
}
2、AService
@Service
public class AService {
@Autowire
private BService bservice;
@Override
public void mainFunc3() {
logger.info("主方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
bservice.sync3();
logger.info("主方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
}
}
3、BService
@Service
public class BService {
@Async
public boolean sync3() {
logger.info("异步方法-" + Thread.currentThread().getName() + ",start:" + System.currentTimeMillis());
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("异步方法-" + Thread.currentThread().getName() + ",end:" + System.currentTimeMillis());
return true;
}
}
//输出结果
org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public boolean com.msedu.distribution.global.service.impl.PromoteConfigServiceImpl.syncReturnFlag()
at org.springframework.aop.framework.CglibAopProxy.processReturnType(CglibAopProxy.java:391)
at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:84)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:690)
at com.msedu.distribution.global.service.impl.BService$$EnhancerBySpringCGLIB$$d60e57a8.syncReturnFlag()
at com.msedu.distribution.global.service.impl.AService.testSync3(TenantProdPromoteServiceImpl.java:331)
//结果
@Async的方法必须是void或者Future,否则报错