Spring Boot 异步任务

Spring 异步任务涉及到两个注解

  • @EnableAsync 标记在配置类上,开启异步任务
  • @Async 标记方法,将方法增强为异步方法
简单示例
  1. 异步任务执行类
@Slf4j
@Component
public class SimpleAsyncTask {

    @Async
    public void execute() {
        log.info("【任务执行】当前线程:{}", Thread.currentThread().getName());
    }

}
  1. 引导程序
@Slf4j
@EnableAsync
@SpringBootApplication
public class SimpleAsyncTaskApplication {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(SimpleAsyncTaskApplication.class, args);
        log.info("【程序启动】当前线程:{}", Thread.currentThread().getName());
        applicationContext.getBean(SimpleAsyncTask.class).execute();
    }

}
  1. 运行程序

控制台打印出如下日志,说明任务执行并没有使用主线程执行,异步任务开启成功

2019-06-03 21:26:33.862  INFO 1150 --- [           main] c.g.l.s.t.a.s.SimpleAsyncTaskApplication : 【程序启动】当前线程:main
2019-06-03 21:26:33.885  INFO 1150 --- [         task-1] c.g.l.s.t.async.simple.SimpleAsyncTask   : 【任务执行】当前线程:task-1

示例代码 https://github.com/ly641921791/knowledge-examples/tree/master/spring-example/spring-task-async-simple

复杂示例

Spring在没有找到线程池时会创建默认的线程池用于处理异常任务,对于异常的任务默认不处理

Spring提供了AsyncConfigurer接口用于配置异步任务使用的线程池和异常处理规则,代码如下

public interface AsyncConfigurer {
	@Nullable
	default Executor getAsyncExecutor() {
		return null;
	}

	@Nullable
	default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return null;
	}
}

实现AsyncConfigurer接口,并将实现类注册到Spring容器就可以达到配置的目的,示例如下

@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {
	// 实现略
}

示例代码 https://github.com/ly641921791/knowledge-examples/tree/master/spring-example/spring-task-async-complex

常见问题
  1. 异步任务失效

原因1:通过this调用的异步方法会失效,因为异步任务的原理是Aop拦截,this调用无法被拦截,可以通过修改配置Aop配置,通过AopContext得到当前对象的代理对象

原因2:通过new得到的对象异步方法失效,因为没有被Spring容器增强

原因3:AsyncConfigurer及子类中的异步方法失效,因为这些类在异步任务处理之前已经创建完成,无法得到二次处理

源码解析
  1. @EnableAsync

@EnableAsync通过@Import引入了AsyncConfigurationSelector,代码如下

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
}
  1. AsyncConfigurationSelector
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}

}

AdviceMode是@EnableAsync的一个属性,默认值是AdviceMode.PROXY,也就是默认情况下,会引入ProxyAsyncConfiguration

  1. ProxyAsyncConfiguration

ProxyAsyncConfiguration做了三件事,代码如下

  • 通过setImportMetadata方法得到了@EnableAsync注解的属性信息,
  • 通过setConfigurers方法得到了AsyncConfigurer配置的线程池和异常处理器,
  • 通过asyncAdvisor方法配置并引入了AsyncAnnotationBeanPostProcessor
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	/*  继承自 AbstractAsyncConfiguration
		@Nullable
		protected AnnotationAttributes enableAsync;
	
		@Nullable
		protected Supplier<Executor> executor;
	
		@Nullable
		protected Supplier<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());
			}
		}
	
		@Autowired(required = false)
		void setConfigurers(Collection<AsyncConfigurer> configurers) {
			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;
		}
	*/

	@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
		Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
		AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
		bpp.configure(this.executor, this.exceptionHandler);
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
			bpp.setAsyncAnnotationType(customAsyncAnnotation);
		}
		bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
		bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
		return bpp;
	}

}
  1. AsyncAnnotationBeanPostProcessor

AsyncAnnotationBeanPostProcessor

  • 通过setBeanFactory方法创建了AsyncAnnotationAdvisor
  • 通过postProcessAfterInitialization创建了Bean的代理对象,并将AsyncAnnotationAdvisor作为Advisor,

AsyncAnnotationAdvisorAnnotationAsyncExecutionInterceptor作为方法拦截器,拦截目标方法执行,将其交过线程池执行实现异步调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值