@Async注解的失效之謎

上一篇 <<<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";
    }
}

訪問404

@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適配器類型匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 有時(shí)候,前端可能提交了一個(gè)耗時(shí)任務(wù),如果后端接收到請(qǐng)求后,直接執(zhí)行該耗時(shí)任務(wù),那么前端需要等待很久一段時(shí)間才...
    Whyn閱讀 8,506評(píng)論 0 3
  • 背景項(xiàng)目中有任何需要花費(fèi)比較長的時(shí)間才可以返回結(jié)果,時(shí)間幾十秒到幾小時(shí)不等,時(shí)間過長前端會(huì)一直處于等待消息返回的狀...
    閑的蛋疼VIP閱讀 11,385評(píng)論 0 0
  • 1. 同步處理 創(chuàng)建Controller: 創(chuàng)建Service: 后臺(tái)打印結(jié)果: 前端結(jié)果: 2. 使用異步 2....
    泓落飛涯閱讀 1,245評(píng)論 0 1
  • 一點(diǎn)知識(shí) 在JAVA開發(fā)領(lǐng)域,目前可以通過以下幾種方式進(jìn)行定時(shí)任務(wù): Timer:jdk中自帶的一個(gè)定時(shí)調(diào)度類,可...
    雅倩蘭爸爸閱讀 3,927評(píng)論 1 28
  • 漸變的面目拼圖要我怎么拼? 我是疲乏了還是投降了? 不是不允許自己墜落, 我沒有滴水不進(jìn)的保護(hù)膜。 就是害怕變得面...
    悶熱當(dāng)乘涼閱讀 4,480評(píng)論 0 13

友情鏈接更多精彩內(nèi)容