Springboot使用@EnableAsync @Async實現異步調用——SpringBoot學習

  SpringBoot 提供了註解 @EnableAsync + @Async 實現方法的異步調用。使用方法超級簡單,在啓動類上加上 @EnableAsync 註解開啓項目的異步調用功能,再在需異步調用的方法上加上註解 @Async 即可實現方法的異步調用。是不是能簡單?簡單吧。

  接來下爲使大家能夠深刻理解異步調用,我將通過實現調用普通方法,使用 @EnableAsync + @Async 實現異步調用方法,和不使用 @EnableAsync + @Async 實現異步調用方法等三中國方式來說明。

一、編寫 Service

  首先需要編寫一個 Service 和 ServiceImpl,新建 src/main/java/com/service/AsyncService.java

package com.service;
/**
* @Description @Async 使用
* @author 歐陽
* @since 2019年4月14日 上午11:50:34
* @version V1.0
*/

public interface AsyncService {

	/**
	 * 描述:普通的方法
	 * @return
	 */
	public String commMethod();
	
	/**
	 * 描述:使用  @Async 實現異步調用
	 * @return
	 */
	public String asyncMethod();
	
	/**
	 * 描述:使用 Callable 實現異步調用
	 * @return
	 */
	public String callableMethod();
}

   src/main/java/com/service/impl/AsyncServiceImpl.java

package com.service.impl;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.service.AsyncService;

/**
* @Description @Async 使用
* @author 歐陽
* @since 2019年4月14日 上午11:51:07
* @version V1.0
*/

@Service
public class AsyncServiceImpl implements AsyncService {
	
	private static final Logger log = LoggerFactory.getLogger(AsyncServiceImpl.class);
	
	private static final String result = "SUCCUSS";	//返回結果
	
	@Override
	public String commMethod() {
		
		log.info("2");
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		log.info("3");
		
		return result;
	}
	
	@Override
	@Async
	public String asyncMethod() {
		
		log.info("2");
		
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		log.info("3");
		
		return result;
	}

	@Override
	public String callableMethod() {
		
		class MyCallable implements Callable<String> {
	        @Override
			public String call() throws Exception {
	        	
	        	log.info("2");
	    		
	    		try {
	    			Thread.sleep(3000);
	    		} catch (InterruptedException e) {
	    			e.printStackTrace();
	    		}
	    		log.info("3");
	    		
	    		return result;
	        }
	    }
		
		ExecutorService pool = Executors.newFixedThreadPool(1);
		Future<String> submit = pool.submit(new MyCallable());
		String result = null;
		
		if(submit.isDone()) {
			try {
				result = submit.get();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		// 關閉線程池
        pool.shutdown();
        
        return result;
	}

}

  其中 commMethod 方法是我們平時用的普通方法;asyncMethod 方法是我們用 @Async 註解的方法,能夠實現異步調用;callableMethod 方法是通過不使用 @Async 註解實現異步調用的方法。

二、編寫 Controller

  爲了直觀的測試代碼,也爲了方便大家理解,就選擇編寫一個 Controller 來進行測試,這裏說一下還可以使用 SpringBoot 整合好的 單元測試功能來測試,可以參考我之前寫的文章,在 SpringBoot 整合持久層技術的時候有用到。編寫的 Controller 代碼如下:

package com.controller;
/**
* @Description @Async 使用
* @author 歐陽
* @since 2019年4月14日 上午11:49:53
* @version V1.0
*/

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.service.AsyncService;

@RestController
public class AsyncController {
	
	private static final Logger log = LoggerFactory.getLogger(AsyncController.class);
	
	@Autowired
	private AsyncService asyncService;
	
	@RequestMapping("/commMethod")
	public String commMethod() {
		log.info("1");
		String result = asyncService.commMethod();
		log.info("4");
		
		return result;
	}
	
	@RequestMapping("/asyncMethod")
	public String asyncMethod() {
		log.info("1");
		String result = asyncService.asyncMethod();
		log.info("4");
		
		return result;
	}
	
	@RequestMapping("/callableMethod")
	public String callableMethod() {
		log.info("1");
		String result = asyncService.callableMethod();
		log.info("4");
		
		return result;
	}
}

三、修改啓動類

  在啓動類上加上註解 @EnableAsync 後啓動項目即可。

@SpringBootApplication
@EnableAsync
public class App {

	public static void main(String[] args) {
		
		SpringApplication.run(App.class, args);
	}

}

四、測試結果分析

  首頁訪問 URL http://localhost:8080/commMethod ,這個 URL 是普通的沒有實現異步的,觀察控制檯打印信息:

2019-04-14 13:10:25.718  INFO 2332 --- [nio-8080-exec-7] com.controller.AsyncController           : 1
2019-04-14 13:10:25.719  INFO 2332 --- [nio-8080-exec-7] com.service.impl.AsyncServiceImpl        : 2
2019-04-14 13:10:28.719  INFO 2332 --- [nio-8080-exec-7] com.service.impl.AsyncServiceImpl        : 3
2019-04-14 13:10:28.719  INFO 2332 --- [nio-8080-exec-7] com.controller.AsyncController           : 4

  注意這裏的打印順序是 1 2 3 4 。說明 Controller 中的 commMethod 方法是按照程序執行順序順序往下執行,在請求該 URL 後切回 Eclipse 觀察控制檯輸出還發現 打印的 2 和 3 中間有時間間隔,這是因爲在中間有 Thread.sleep(3000); 三秒的休眠時間;同時到最後 4 打印完後瀏覽器顯示 SUCCUSS 的字樣。

  接着我們訪問 URL http://localhost:8080/asyncMethod,這個 URL 是使用 @Async 註解的方法,觀察控制檯打印信息:

2019-04-14 13:15:31.192  INFO 2332 --- [nio-8080-exec-1] com.controller.AsyncController           : 1
2019-04-14 13:15:31.192  INFO 2332 --- [nio-8080-exec-1] com.controller.AsyncController           : 4
2019-04-14 13:15:31.192  INFO 2332 --- [tTaskExecutor-1] com.service.impl.AsyncServiceImpl        : 2
2019-04-14 13:15:34.193  INFO 2332 --- [tTaskExecutor-1] com.service.impl.AsyncServiceImpl        : 3

  我們發現這次打印的順序是 1 4 2 3。說明 Controller 中的 asyncMethod 方法不是按找順序執行的,而是先執行 1 4 ,再執行 2 3 ,,同時我們可以觀察到瀏覽器頁面上什麼都沒有,沒有顯示 SUCCUSS 的字樣,也就是說調用的 asyncService 中的 asyncMethod 方法是異步的。

  這裏說報一個警告 :No task executor bean found for async processing: no bean of type TaskExecutor and no bean named ‘taskExecutor’ either 。這個警告意思是說 Spring 容器中 沒有 TaskExecutor ,我們可以注入一個

package com.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
* @Description 
*   注入 TaskExecutor 解決 No task executor bean found for async processing: 
* 	no bean of type TaskExecutor and no bean named ‘taskExecutor’ either
* @author 歐陽
* @since 2019年4月14日 下午12:28:11
* @version V1.0
*/

@Configuration
public class TaskExecutorBean {
	
	@Bean
	public TaskExecutor getTaskExecutor() {
		
		return new ThreadPoolTaskExecutor();
	}
}

  最後我們訪問 http://localhost:8080/callableMethod ,發現其效果和訪問 http://localhost:8080/asyncMethod 的是一模一樣的,這說明也可以使用多線程技術實現 @EnableAsync + @Async 的效果,但是我們也發現使用多線程技術實現的異步調用的代碼量遠比註解方式要多,所以在一般場景下建議使用 SpringBoot 提供的 註解方式 實現異步調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章