在一些后臺耗時的場景比如說生成統計報表,生成數據文件,執行批量任務時候,需要異步執行,先登記信息,然后異步執行批量同步返回給客戶端。在spring中要想使用異步方法執行,必須使用@EnableAsync注解開啟async。
開啟異步執行配置
@Configuration
@EnableAsync
@ComponentScan(basePackages = {"com.cpx.service.async"})
public class AsyncConfig {}
然后在需要異步執行的方法上添加@Async注解
@Service
public class AsyncService {@Asyncpublic void exec(){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(LocalDateTime.now().toString() +":exec end");}
}
添加@Async注解的方法必須是public的。方法所屬的類必須是一個bean才能被掃描到。this方式調用異步方法是沒有效果的。因為使用的動態代理。
測試方法的異步執行:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AsyncConfig.class);
AsyncService asyncService = (AsyncService) ctx.getBean("asyncService");
System.out.println(LocalDateTime.now().toString()+" start");
asyncService.exec();//該方法調用會異步執行
System.out.println(LocalDateTime.now().toString()+" main end");
ctx.close();
TaskExecutor
方法的異步執行是通過線程池來執行的, spring默認會從beanFactory中嘗試去獲取TaskExecutor類型的bean,如果獲取不到,就會繼續嘗試獲取bean name為’taskExecutor’的bean,還獲取不到就創建一個SimpleAsyncTaskExecutor類型的執行器。SimpleAsyncTaskExecutor每來一個任務會啟動一個Thread來執行任務,不會重復利用。這么看SimpleAsyncTaskExecutor不是一個嚴格意義的線程池,線程并不會重用。如果高并發耗時任務可能會導致一定的內存使用問題。這里可以指定線程池。
第一種通過實現AsyncConfigurer接口,然后重寫getAsyncExecutor()方法。
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {//自定義線程池實現ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.initialize();return threadPoolTaskExecutor;}
}
這種線程池是所有@Async注解都可以使用,另一種可以在@Async(“taskExecutor”)注解上指定線程池名字。
返回值
可以返回一個future。
@Async
public Future<String> exec(){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return new AsyncResult<String>("ok");
}
異常處理
有返回的@Async方法異常會被封裝到future里,沒有返回值的可以定義一個AsyncUncaughtExceptionHandler來處理異常
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {System.out.println(method.getName()+",發生異常");//TODO}
}
然后在實現AsyncConfigurer的bean里重寫getAsyncUncaughtExceptionHandler()返回自定義的異常handler。
異步執行的原理還是通過@EnableAsync引入對應的bean后置處理器,來使用advisor對@Async標識的方法進行代理增強。方法的執行會通過AsyncExecutionInterceptor代理執行。