1. 程式人生 > 程式設計 >Spring @async方法如何添加註解實現非同步呼叫

Spring @async方法如何添加註解實現非同步呼叫

這篇文章主要介紹了Spring @async方法如何添加註解實現非同步呼叫,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

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

spring 在掃描bean的時候會掃描方法上是否包含@async的註解,如果包含的,spring會為這個bean動態的生成一個子類,我們稱之為代理類(?), 代理類是繼承我們所寫的bean的,然後把代理類注入進來,那此時,在執行此方法的時候,會到代理類中,代理類判斷了此方法需要非同步執行,就不會呼叫父類 (我們原本寫的bean)的對應方法。spring自己維護了一個佇列,他會把需要執行的方法,放入佇列中,等待執行緒池去讀取這個佇列,完成方法的執行, 從而完成了非同步的功能。

我們可以關注到再配置task的時候,是有引數讓我們配置執行緒池的數量的。因為這種實現方法,所以在同一個類中的方法呼叫,新增@async註解是失效的!,原因是當你在同一個類中的時候,方法呼叫是在類體內執行的,spring無法截獲這個方法呼叫。

那在深入一步,spring為我們提供了AOP,面向切面的功能。他的原理和非同步註解的原理是類似的,spring在啟動容器的時候,會掃描切面所定義的 類。在這些類被注入的時候,所注入的也是代理類,當你呼叫這些方法的時候,本質上是呼叫的代理類。通過代理類再去執行父類相對應的方法,那spring只 需要在呼叫之前和之後執行某段程式碼就完成了AOP的實現了!

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

簡單介紹:

Spring為任務排程與非同步方法執行提供了註解支援。通過在方法上設定@Async註解,可使得方法被非同步呼叫。也就是說呼叫者會在呼叫時立即返回,而被呼叫方法的實際執行是交給Spring的TaskExecutor來完成。

開啟@Async註解:

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

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

為了比較,先來一個同步呼叫:

@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);// 不讓主程序過早結束
  }
}

輸出結果:

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

非同步呼叫,通過開啟新的執行緒來執行呼叫的方法,不影響主執行緒。非同步方法實際的執行交給了Spring的TaskExecutor來完成。

上面這種方式是沒有返回值的,下面嘗試有返回值的非同步呼叫:

@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());
  }
}

輸出結果:

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

傳送訊息用了2秒

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。