@async 方法上添加該註解實現異步調用的原理

在咱們使用spring框架的過程當中,在不少時候咱們會使用@async註解來異步執行某一些方法,提升系統的執行效率。今天咱們來探討下 spring 是如何完成這個功能的。

    spring 在掃描bean的時候會掃描方法上是否包含@async的註解,若是包含的,spring會爲這個bean動態的生成一個子類,咱們稱之爲代理類(?), 代理類是繼承咱們所寫的bean的,而後把代理類注入進來,那此時,在執行此方法的時候,會到代理類中,代理類判斷了此方法須要異步執行,就不會調用父類 (咱們本來寫的bean)的對應方法。spring本身維護了一個隊列,他會把須要執行的方法,放入隊列中,等待線程池去讀取這個隊列,完成方法的執行, 從而完成了異步的功能。咱們能夠關注到再配置task的時候,是有參數讓咱們配置線程池的數量的。由於這種實現方法,因此在同一個類中的方法調用,添加@async註解是失效的!,緣由是當你在同一個類中的時候,方法調用是在類體內執行的,spring沒法截獲這個方法調用。

    那在深刻一步,spring爲咱們提供了AOP,面向切面的功能。他的原理和異步註解的原理是相似的,spring在啓動容器的時候,會掃描切面所定義的 類。在這些類被注入的時候,所注入的也是代理類,當你調用這些方法的時候,本質上是調用的代理類。經過代理類再去執行父類相對應的方法,那spring只 須要在調用以前和以後執行某段代碼就完成了AOP的實現了!

   那最後咱們還有一個問題,spring是如何動態的生成某一個類的子類的?代理類?spring

簡單介紹:

Spring爲任務調度與異步方法執行提供了註解支持。經過在方法上設置@Async註解,可以使得方法被異步調用。也就是說調用者會在調用時當即返回,而被調用方法的實際執行是交給Spring的TaskExecutor來完成。網絡

開啓@Async註解:

<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 註解 -->
<task:executor id="annotationExecutor" pool-size="20"/>

 

同時加入<context:component-scan />掃描註解。app

 

爲了比較,先來一個同步調用:

@Component
public class TestAsyncBean {
    public void sayHello4() throws InterruptedException {
        Thread.sleep(2 * 1000);//網絡鏈接中 。。。消息發送中。。。
        System.out.println("我愛你啊!");
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
    @Test
    public void test_sayHello4() throws InterruptedException, ExecutionException {
        System.out.println("你不愛我了麼?");
        testAsyncBean.sayHello4();
        System.out.println("回的這麼慢, 你確定不愛我了, 咱們仍是分手吧。。。");
        Thread.sleep(3 * 1000);// 不讓主進程過早結束
    }
}

輸出結果:框架

你不愛我了麼?
我愛你啊!
回的這麼慢, 你確定不愛我了, 咱們仍是分手吧。。。 

同步調用會按代碼順序依次進行下去,若是哪裏須要等待,那麼就阻塞在那裏,再也不向下繼續進行。 異步

使用@Async的異步調用: 

@Component
public class TestAsyncBean {
    @Async
    public void sayHello3() throws InterruptedException {
        Thread.sleep(2 * 1000);//網絡鏈接中 。。。消息發送中。。。
        System.out.println("我愛你啊!");
    }
}

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
    @Autowired
    private TestAsyncBean testAsyncBean;
    @Test
    public void test_sayHello3() throws InterruptedException, ExecutionException {
        System.out.println("你不愛我了麼?");
        testAsyncBean.sayHello3();
        System.out.println("你竟無話可說, 咱們分手吧。。。");
        Thread.sleep(3 * 1000);// 不讓主進程過早結束
    }
} 

輸出結果:async

你不愛我了麼?
你竟無話可說, 咱們分手吧。。。
我愛你啊!

異步調用,經過開啓新的線程來執行調用的方法,不影響主線程。異步方法實際的執行交給了Spring的TaskExecutor來完成。spa

上面這種方式是沒有返回值的,下面嘗試有返回值的異步調用: 

@Component
public class TestAsyncBean {
    @Async
    public String sayHello2() throws InterruptedException {
        Thread.sleep(2 * 1000);//網絡鏈接中 。。。消息發送中。。。
        return "我愛你啊!";// 調用方調用後會當即返回,因此返回null
    }
}

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
    @Autowired
    private TestAsyncBean testAsyncBean;
    @Test
    public void test_sayHello2() throws InterruptedException, ExecutionException {
        System.out.println("你不愛我了麼?");
        System.out.println(testAsyncBean.sayHello2());
        System.out.println("你說的啥? 咱們仍是分手吧。。。");
        Thread.sleep(3 * 1000);// 不讓主進程過早結束
    }
}

輸出結果: 線程

你不愛我了麼?
null
你說的啥? 咱們仍是分手吧。。。

經過直接獲取返回值得方式是不行的,這裏就須要用到異步回調,異步方法返回值必須爲Future<>,就像Callable與Future。代理

下面經過AsyncResult<>來得到異步調用的返回值:

@Component
public class TestAsyncBean {
    @Async
    public Future<String> sayHello1() throws InterruptedException {
        int thinking = 2;
        Thread.sleep(thinking * 1000);//網絡鏈接中 。。。消息發送中。。。
        System.out.println("我愛你啊!");
        return new AsyncResult<String>("發送消息用了"+thinking+"秒");
    }
}

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/applicationContext.xml"})
public class TestAsync {
    @Autowired
    private TestAsyncBean testAsyncBean;
    @Test
    public void test_sayHello1() throws InterruptedException, ExecutionException {
        Future<String> future = null;
        System.out.println("你不愛我了麼?");
        future = testAsyncBean.sayHello1();
        System.out.println("你竟無話可說, 咱們分手吧。。。");
        Thread.sleep(3 * 1000);// 不讓主進程過早結束
        System.out.println(future.get());
    }
}

輸出結果:code

你不愛我了麼?
你竟無話可說, 咱們分手吧。。。
我愛你啊!
發送消息用了2秒
相關文章
相關標籤/搜索