上一篇 <<<Java基础-字节码技术
下一篇 >>>装饰模式(Decorator Pattern)
@Async如何使用
- 异步的方法上加上@Async异步注解
- 启动类中需要加上@EnableAsync才有效
使用时类似于下列函数:
new Thread(()-> System.out.println("hello world !"))
@Async线程池
- 默认线程池
无论重复多少次,都默认8个左右的线程在跑
异步线程:task-1执行成功
异步线程:task-2执行成功
异步线程:task-3执行成功
异步线程:task-4执行成功
异步线程:task-5执行成功
异步线程:task-6执行成功
异步线程:task-7执行成功
异步线程:task-1执行成功
异步线程:task-2执行成功
异步线程:task-8执行成功
异步线程:task-3执行成功
异步线程:task-8执行成功
异步线程:task-6执行成功
异步线程:task-8执行成功
异步线程:task-5执行成功
异步线程:task-3执行成功
异步线程:task-2执行成功
异步线程:task-1执行成功 - 自定义线程池配置
/**
* 配置类实现AsyncConfigurer接口并重写getAsyncExcutor方法,并返回一个ThreadPoolTaskExevutor
* 这样我们就给Async配置自定义线程池
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
taskExecutor.initialize();
return taskExecutor;
}
异步线程:ThreadPoolTaskExecutor-2执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功
异步线程:ThreadPoolTaskExecutor-2执行成功
异步线程:ThreadPoolTaskExecutor-1执行成功
异步线程:ThreadPoolTaskExecutor-3执行成功
@Async和@Controller同时使用存在异常
AbstractHandlerMethodMapping初始bean时,在afterPropertiesSet方法中initHandlerMethods()关联我们的SpringMVCBean
// 类型判断
if (beanType != null && isHandler(beanType)) {
// url处理【只有执行到此处,才能加入到SpringMVC的容器中,才能使用http调用到】
detectHandlerMethods(beanName);
}
// Controller和RequestMapping注解判断
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
a、@Controller不使用@Async注解时
使用异步才会开启代理类,否则一直是目标类。
@RestController
@Slf4j
public class MemberServiceImpl1 {
@GetMapping("/addUser1")
public String addUser() {
log.info(">>>流程1");
log.info(">>>流程2");
return "success";
}
}
b、@Controller使用@Async注解,但不继承接口时,异步失效
采用Cglib生成的代理对象继承了目标对象@RestController注解,这时候Cglib生成的代理对象是可以注入到SpringMVC容器中。
执行时使用this后没有走代理类,没有走拦截,自然异步注解失效。
Springboot启动时加上该参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true可看到所有的代理类。
@RestController
@Slf4j
public class MemberServiceImpl3 {
@GetMapping("/addUser3")
public String addUser() {
log.info(">>>流程1");
addUserLog();
log.info(">>>流程3");
return "success";
}
@Async()
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
c、@Controller使用@Async注解,同时继承接口
使用了接口就会使用jdk动态代理,而代理类是没有@Controller注解的,自然无法加入到SpringMVC容器,进而请求接口就会报错。
@RestController
@Slf4j
public class MemberServiceImpl4 implements MemberService {
@Override
@GetMapping("/addUser4")
public String addUser() {
log.info(">>>流程1");
addUserLog();
log.info(">>>流程3");
return "success";
}
@Async()
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
@Async注解失效场景
1.注解@Async的方法不是public方法
2.注解@Async的返回值只能为void或Future
3.注解@Async方法使用static修饰也会失效
4.spring无法扫描到异步类,没加注解@Async或@EnableAsync注解
5.调用方与被调用方不能在同一个类
6.类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
7.在Async方法上标注@Transactional是没用的.但在Async方法调用的方法上标注@Transcational是有效的
@Async使用建议
- a、异步执行的建议单独开启一个类实现,或者从容器中直接获取到该代理类后执行
/**
* 异步代码写到别的地方,不要和Controller注解同时使用
*/
@RestController
@Slf4j
public class ResolveServiceImpl {
@Autowired
private MemberServiceManage memberServiceManage;
@GetMapping("/resolve2")
public String addUser() {
log.info(">>>流程1");
memberServiceManage.addUserLog();
log.info(">>>流程3");
return "success";
}
}
@Component
@Slf4j
public class MemberServiceManage {
@Async
public String addUserLog() {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
log.info(">>>流程2");
return "success";
}
}
/**
* 使用SpringUtils获得bean后调用,不要使用this操作
*/
@GetMapping("/resolve1")
public String addUser() {
log.info(">>>流程1");
//把直接调用改为从容器中取一次
ResolveServiceImpl bean = SpringUtils.getBean(ResolveServiceImpl.class);
bean.addUserLog();
log.info(">>>流程3");
return "success";
}
- b、异步的方法上不要加static,加了static就不走AOP了
推荐阅读:
<<<Spring Servlet相关知识
<<<Spring原理汇总及零碎知识点
<<<Web项目的启动方式汇总
<<<SpringMVC底层无web.xml启动原理分析
<<<SpringMVC运行流程
<<<DispatcherServlet执行原理分析
<<<过滤器与拦截器的区别
<<<SpringMVC拦截器的用法
<<<SpringMVC异步实现方式
<<<SpringMVC适配器类型汇总