摘要:序本文主要聊聊中的注解。這里從獲取注解有個可以標注使用哪個,這里的就是尋找這個標識。推薦注解指定,然后的返回,讓它去尋找默認的自己應用里頭都默認定義一個給托管
序
本文主要聊聊spring中的async注解。
AsyncConfigurer@EnableAsync(proxyTargetClass = true) @Configuration public class AsyncConfig implements AsyncConfigurer{ private static final Logger LOGGER = LoggerFactory.getLogger(AsyncConfig.class); @Override public Executor getAsyncExecutor() { return null; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... params) { LOGGER.error(throwable); } }; } }getAsyncExecutor
這里討論一下getAsyncExecutor這里定義null的情況。
spring-context-4.3.9.RELEASE-sources.jar!/org/springframework/scheduling/annotation/AbstractAsyncConfiguration.java
@Configuration public abstract class AbstractAsyncConfiguration implements ImportAware { protected AnnotationAttributes enableAsync; protected Executor executor; protected AsyncUncaughtExceptionHandler exceptionHandler; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableAsync = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false)); if (this.enableAsync == null) { throw new IllegalArgumentException( "@EnableAsync is not present on importing class " + importMetadata.getClassName()); } } /** * Collect any {@link AsyncConfigurer} beans through autowiring. */ @Autowired(required = false) void setConfigurers(Collectionconfigurers) { if (CollectionUtils.isEmpty(configurers)) { return; } if (configurers.size() > 1) { throw new IllegalStateException("Only one AsyncConfigurer may exist"); } AsyncConfigurer configurer = configurers.iterator().next(); this.executor = configurer.getAsyncExecutor(); this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler(); } }
AsyncExecutionInterceptor這里從AsyncConfigurer獲取executor
spring-aop-4.3.9.RELEASE-sources.jar!/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
/** * Intercept the given method invocation, submit the actual calling of the method to * the correct task executor and return immediately to the caller. * @param invocation the method to intercept and make asynchronous * @return {@link Future} if the original method returns {@code Future}; {@code null} * otherwise. */ @Override public Object invoke(final MethodInvocation invocation) throws Throwable { Class> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); if (executor == null) { throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } CallableAsyncExecutionAspectSupport.determineAsyncExecutor
spring-aop-4.3.9.RELEASE-sources.jar!/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java
** * Determine the specific executor to use when executing the given method. * Should preferably return an {@link AsyncListenableTaskExecutor} implementation. * @return the executor to use (or {@code null}, but just if no default executor is available) */ protected AsyncTaskExecutor determineAsyncExecutor(Method method) { AsyncTaskExecutor executor = this.executors.get(method); if (executor == null) { Executor targetExecutor; String qualifier = getExecutorQualifier(method); if (StringUtils.hasLength(qualifier)) { targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier); } else { targetExecutor = this.defaultExecutor; if (targetExecutor == null) { synchronized (this.executors) { if (this.defaultExecutor == null) { this.defaultExecutor = getDefaultExecutor(this.beanFactory); } targetExecutor = this.defaultExecutor; } } } if (targetExecutor == null) { return null; } executor = (targetExecutor instanceof AsyncListenableTaskExecutor ? (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor)); this.executors.put(method, executor); } return executor; }
@Aync注解有個value可以標注使用哪個executor,這里的getExecutorQualifier就是尋找這個標識。
這里如果defaultExecutor為null的話,則獲取找默認的executor
/** * Retrieve or build a default executor for this advice instance. * An executor returned from here will be cached for further use. *The default implementation searches for a unique {@link TaskExecutor} bean * in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. * If neither of the two is resolvable, this implementation will return {@code null}. * @param beanFactory the BeanFactory to use for a default executor lookup * @return the default executor, or {@code null} if none available * @since 4.2.6 * @see #findQualifiedExecutor(BeanFactory, String) * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME */ protected Executor getDefaultExecutor(BeanFactory beanFactory) { if (beanFactory != null) { try { // Search for TaskExecutor bean... not plain Executor since that would // match with ScheduledExecutorService as well, which is unusable for // our purposes here. TaskExecutor is more clearly designed for it. return beanFactory.getBean(TaskExecutor.class); } catch (NoUniqueBeanDefinitionException ex) { logger.debug("Could not find unique TaskExecutor bean", ex); try { return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { if (logger.isInfoEnabled()) { logger.info("More than one TaskExecutor bean found within the context, and none is named " + ""taskExecutor". Mark one of them as primary or name it "taskExecutor" (possibly " + "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound()); } } } catch (NoSuchBeanDefinitionException ex) { logger.debug("Could not find default TaskExecutor bean", ex); try { return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); } catch (NoSuchBeanDefinitionException ex2) { logger.info("No task executor bean found for async processing: " + "no bean of type TaskExecutor and no bean named "taskExecutor" either"); } // Giving up -> either using local default executor or none at all... } } return null; }
AsyncExecutionInterceptor.getDefaultExecutor如果工程里頭沒有定義默認的task executor的話,則獲取bean的時候會拋出NoSuchBeanDefinitionException
spring-aop-4.3.9.RELEASE-sources.jar!/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
protected Executor getDefaultExecutor(BeanFactory beanFactory) { Executor defaultExecutor = super.getDefaultExecutor(beanFactory); return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); }
Executor關閉問題AsyncExecutionInterceptor重寫了getDefaultExecutor方法,先調用AsyncExecutionAspectSupport的getDefaultExecutor,如果默認的找不到,這里new一個SimpleAsyncTaskExecutor
如果是在AsyncConfigurer定義的executor,沒有受spring托管,貌似是不會在spring context關閉的時候主動shutdown,這個可能是個問題。
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements AsyncListenableTaskExecutor, SchedulingTaskExecutor { //... }
spring-context-4.3.9.RELEASE-sources.jar!/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java
public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory implements BeanNameAware, InitializingBean, DisposableBean { //... /** * Perform a shutdown on the underlying ExecutorService. * @see java.util.concurrent.ExecutorService#shutdown() * @see java.util.concurrent.ExecutorService#shutdownNow() * @see #awaitTerminationIfNecessary() */ public void shutdown() { if (logger.isInfoEnabled()) { logger.info("Shutting down ExecutorService" + (this.beanName != null ? " "" + this.beanName + """ : "")); } if (this.waitForTasksToCompleteOnShutdown) { this.executor.shutdown(); } else { this.executor.shutdownNow(); } awaitTerminationIfNecessary(); } /** * Wait for the executor to terminate, according to the value of the * {@link #setAwaitTerminationSeconds "awaitTerminationSeconds"} property. */ private void awaitTerminationIfNecessary() { if (this.awaitTerminationSeconds > 0) { try { if (!this.executor.awaitTermination(this.awaitTerminationSeconds, TimeUnit.SECONDS)) { if (logger.isWarnEnabled()) { logger.warn("Timed out while waiting for executor" + (this.beanName != null ? " "" + this.beanName + """ : "") + " to terminate"); } } } catch (InterruptedException ex) { if (logger.isWarnEnabled()) { logger.warn("Interrupted while waiting for executor" + (this.beanName != null ? " "" + this.beanName + """ : "") + " to terminate"); } Thread.currentThread().interrupt(); } } } }
ExecutorConfigurationSupport實現了DisposableBean接口,重寫了destory方法,在里頭調用shutdown
因此,最好將ThreadPoolTaskExecutor的定義托管給spring,這樣可以優化關閉。
小結 async注解沒有指定executor如果AsyncConfigurer沒有定義executor,則會去尋找spring托管的名為taskExecutor的executor,如果沒有,則拋出NoSuchBeanDefinitionException,返回null,然后由AsyncExecutionInterceptor.getDefaultExecutor去new一個SimpleAsyncTaskExecutor,不過這個不是spring托管的
如果AsyncConfigurer定義了executor,則這個也不是spring托管的
async注解指定executor不是spring托管的executor,需要自己額外去監聽事件,然后優雅關閉
比如
@Async("myTaskExecutor") public void xxxx(){ }
這個則使用指定的myTaskExecutor,而不是AsyncConfigurer中定義的executor。
doc推薦async注解指定task executor,然后AsyncConfigurer的getAsyncExecutor返回null,讓它去尋找默認的taskExecutor(自己應用里頭都默認定義一個taskExecutor給spring托管)
Task Execution and Scheduling
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/70551.html
摘要:今天我想聊聊的另一個很棒的特性就是它的可擴展性。的擴展機制在的官網上,描述自己是一個高性能的框架。接下來的章節中我們會慢慢揭開擴展機制的神秘面紗。擴展擴展點的實現類。的定義在配置文件中可以看到文件中定義了個的擴展實現。 摘要: 在Dubbo的官網上,Dubbo描述自己是一個高性能的RPC框架。今天我想聊聊Dubbo的另一個很棒的特性, 就是它的可擴展性。 Dubbo的擴展機制 在Dub...
摘要:耗時毫秒耗時毫秒耗時毫秒添加異步任務控制器測試異步任務控制器。 SpringCloud(第 047 篇)注解式Async配置異步任務 - 一、大致介紹 1、有時候我們在處理一些任務的時候,需要開啟線程去異步去處理,原有邏輯繼續往下執行; 2、當遇到這種場景的時候,線程是可以將我們完成,然后在SpringCloud中也有這樣的注解來支撐異步任務處理; 二、實現步驟 2.1 添加 mave...
摘要:下面我們稍稍改下代碼來證實一下這次我讓任務執行的時間等于,大于條線程總間隔時間來耗盡線程池中的線程。 1.開篇 在Spring定時任務@Scheduled注解使用方式淺窺這篇文章里面提及過,spring的定時任務默認是單線程的,他在某些場景下會造成堵塞,那么如果我們想讓每一個任務都起一條線程去執行呢? 2.使用@Async 我們可以使用Spring的@Async注解十分容易的實現多線程...
摘要:面試官要不你來手寫下單例模式唄候選者單例模式一般會有好幾種寫法候選者餓漢式簡單懶漢式在方法聲明時加鎖雙重檢驗加鎖進階懶漢式靜態內部類優雅懶漢式枚舉候選者所謂餓漢式指的就是還沒被用到,就直接初始化了對象。面試官:我看你的簡歷寫著熟悉常見的設計模式,要不你來簡單聊聊你熟悉哪幾個吧?候選者:常見的工廠模式、代理模式、模板方法模式、責任鏈模式、單例模式、包裝設計模式、策略模式等都是有所了解的候選者:...
摘要:最近遇到一個需求,就是當服務器接到請求并不需要任務執行完成才返回結果,可以立即返回結果,讓任務異步的去執行。指定從上面執行的日志可以猜測到默認使用來異步執行任務的,可以搜索到這個類。 最近遇到一個需求,就是當服務器接到請求并不需要任務執行完成才返回結果,可以立即返回結果,讓任務異步的去執行。開始考慮是直接啟一個新的線程去執行任務或者把任務提交到一個線程池去執行,這兩種方法都是可以的。但...
閱讀 1066·2021-11-12 10:34
閱讀 996·2021-09-30 09:56
閱讀 674·2019-08-30 15:54
閱讀 2608·2019-08-30 11:14
閱讀 1473·2019-08-29 16:44
閱讀 3212·2019-08-29 16:35
閱讀 2498·2019-08-29 16:22
閱讀 2448·2019-08-29 15:39