上一篇 <<<Java基礎(chǔ)-字節(jié)碼技術(shù)
下一篇 >>>裝飾模式(Decorator Pattern)
@Async如何使用
- 異步的方法上加上@Async異步注解
- 啟動(dòng)類中需要加上@EnableAsync才有效
使用時(shí)類似于下列函數(shù):
new Thread(()-> System.out.println("hello world !"))
@Async線程池
- 默認(rèn)線程池
無論重復(fù)多少次,都默認(rèn)8個(gè)左右的線程在跑
異步線程:task-1執(zhí)行成功
異步線程:task-2執(zhí)行成功
異步線程:task-3執(zhí)行成功
異步線程:task-4執(zhí)行成功
異步線程:task-5執(zhí)行成功
異步線程:task-6執(zhí)行成功
異步線程:task-7執(zhí)行成功
異步線程:task-1執(zhí)行成功
異步線程:task-2執(zhí)行成功
異步線程:task-8執(zhí)行成功
異步線程:task-3執(zhí)行成功
異步線程:task-8執(zhí)行成功
異步線程:task-6執(zhí)行成功
異步線程:task-8執(zhí)行成功
異步線程:task-5執(zhí)行成功
異步線程:task-3執(zhí)行成功
異步線程:task-2執(zhí)行成功
異步線程:task-1執(zhí)行成功 - 自定義線程池配置
/**
* 配置類實(shí)現(xiàn)AsyncConfigurer接口并重寫getAsyncExcutor方法,并返回一個(gè)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執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-1執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-3執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-1執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-3執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-2執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-1執(zhí)行成功
異步線程:ThreadPoolTaskExecutor-3執(zhí)行成功
@Async和@Controller同時(shí)使用存在異常
AbstractHandlerMethodMapping初始bean時(shí),在afterPropertiesSet方法中initHandlerMethods()關(guān)聯(lián)我們的SpringMVCBean
// 類型判斷
if (beanType != null && isHandler(beanType)) {
// url處理【只有執(zhí)行到此處,才能加入到SpringMVC的容器中,才能使用http調(diào)用到】
detectHandlerMethods(beanName);
}
// Controller和RequestMapping注解判斷
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
a、@Controller不使用@Async注解時(shí)
使用異步才會(huì)開啟代理類,否則一直是目標(biāo)類。
@RestController
@Slf4j
public class MemberServiceImpl1 {
@GetMapping("/addUser1")
public String addUser() {
log.info(">>>流程1");
log.info(">>>流程2");
return "success";
}
}

b、@Controller使用@Async注解,但不繼承接口時(shí),異步失效
采用Cglib生成的代理對(duì)象繼承了目標(biāo)對(duì)象@RestController注解,這時(shí)候Cglib生成的代理對(duì)象是可以注入到SpringMVC容器中。
執(zhí)行時(shí)使用this后沒有走代理類,沒有走攔截,自然異步注解失效。
Springboot啟動(dòng)時(shí)加上該參數(shù):-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注解,同時(shí)繼承接口
使用了接口就會(huì)使用jdk動(dòng)態(tài)代理,而代理類是沒有@Controller注解的,自然無法加入到SpringMVC容器,進(jìn)而請(qǐng)求接口就會(huì)報(bào)錯(cuò)。
@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修飾也會(huì)失效
4.spring無法掃描到異步類,沒加注解@Async或@EnableAsync注解
5.調(diào)用方與被調(diào)用方不能在同一個(gè)類
6.類中需要使用@Autowired或@Resource等注解自動(dòng)注入,不能自己手動(dòng)new對(duì)象
7.在Async方法上標(biāo)注@Transactional是沒用的.但在Async方法調(diào)用的方法上標(biāo)注@Transcational是有效的
@Async使用建議
- a、異步執(zhí)行的建議單獨(dú)開啟一個(gè)類實(shí)現(xiàn),或者從容器中直接獲取到該代理類后執(zhí)行
/**
* 異步代碼寫到別的地方,不要和Controller注解同時(shí)使用
*/
@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后調(diào)用,不要使用this操作
*/
@GetMapping("/resolve1")
public String addUser() {
log.info(">>>流程1");
//把直接調(diào)用改為從容器中取一次
ResolveServiceImpl bean = SpringUtils.getBean(ResolveServiceImpl.class);
bean.addUserLog();
log.info(">>>流程3");
return "success";
}
- b、異步的方法上不要加static,加了static就不走AOP了
推薦閱讀:
<<<Spring Servlet相關(guān)知識(shí)
<<<Spring原理匯總及零碎知識(shí)點(diǎn)
<<<Web項(xiàng)目的啟動(dòng)方式匯總
<<<SpringMVC底層無web.xml啟動(dòng)原理分析
<<<SpringMVC運(yùn)行流程
<<<DispatcherServlet執(zhí)行原理分析
<<<過濾器與攔截器的區(qū)別
<<<SpringMVC攔截器的用法
<<<SpringMVC異步實(shí)現(xiàn)方式
<<<SpringMVC適配器類型匯總