Spring中需要方法异步执行的时候就会用到@Async注解,使用此注解时可以将该函数交予线程池异步执行。
我们首先需要在启动类上加上EnableAsync注解
@SpringBootApplication
@EnableAsync
public class PaymentApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(PaymentApplication.class);
springApplication.run(args);
}
}
源码为
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
/**
* Indicate the 'async' annotation type to be detected at either class
* or method level.
* By default, both Spring's @{@link Async} annotation and the EJB 3.1
* {@code @javax.ejb.Asynchronous} annotation will be detected.
*
This attribute exists so that developers can provide their own
* custom annotation type to indicate that a method (or all methods of
* a given class) should be invoked asynchronously.
*/
Class<? extends Annotation> annotation() default Annotation.class;
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies.
* Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}.
*
The default is {@code false}.
*
Note that setting this attribute to {@code true} will affect all
* Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
* For example, other beans marked with Spring's {@code @Transactional} annotation
* will be upgraded to subclass proxying at the same time. This approach has no
* negative impact in practice unless one is explicitly expecting one type of proxy
* vs. another — for example, in tests.
*/
boolean proxyTargetClass() default false;
/**
* Indicate how async advice should be applied.
* The default is {@link AdviceMode#PROXY}.
* Please note that proxy mode allows for interception of calls through the proxy
* only. Local calls within the same class cannot get intercepted that way; an
* {@link Async} annotation on such a method within a local call will be ignored
* since Spring's interceptor does not even kick in for such a runtime scenario.
* For a more advanced mode of interception, consider switching this to
* {@link AdviceMode#ASPECTJ}.
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
* should be applied.
* The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
* after all other post-processors, so that it can add an advisor to
* existing proxies rather than double-proxy.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
首先,方法org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean
会将启动类包装成org.springframework.beans.factory.config.BeanDefinitionHolder
.在构建Bean的时候会通过方法org.springframework.context.annotation.AnnotationBeanNameGenerator#determineBeanNameFromAnnotation
包装他的注解。然后扫描到EnableAsync注解。
启动类会被 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
当作通过 org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate
过滤后最终得到configCandidates 执行parse操作。
enableAsync也是解析的import来实现其功能的,先通过org.springframework.context.annotation.ConfigurationClassParser#getImports
得到所有import得类,然后执行import中的函数org.springframework.context.annotation.ConfigurationClassParser#processImports
,
在import中可以有3中方式执行注入
- 继承
org.springframework.context.annotation.ImportSelector
接口- 继承
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
接口- 普通类,将被当作config类,就相当于加入
@Configuration
注解,里面的类将会直接执行enableAsync就是采用的第一种方法执行的。
,这个方法的结果是将org.springframework.scheduling.annotation.ProxyAsyncConfiguration
返回,
然后将ProxyAsyncConfiguration,执行processImports的第三种注入方式。然后最终注入org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor
类。
AsyncAnnotationBeanPostProcessor是一个切面的processor。
我们得到AsyncAnnotationBeanPostProcessor,这个类被注入容器。
因为AsyncAnnotationBeanPostProcessor继承了BeanFactoryAware,在注入bean的时候将会执行org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
方法,设置beanFactory,在beanFactory的时候,将会执行构造org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(java.util.function.Supplier
,构造函数会默认将org.springframework.scheduling.annotation.Async
,注解弄成切点。
然后在执行方法的时候,由于使用了代理,他会执行org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
,因为他有切点,他会执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
,这个advice就是org.springframework.aop.interceptor.AsyncExecutionInterceptor
,最终通过方法
org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit
执行
在执行异步任务中,spring通过异步线程来管理的任务的执行。通过方法org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor
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.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
通过这个可以发现,我们可以自定义方法的线程池。
Spring 的默认线程池是由 org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration#taskExecutorBuilder
决定的,我们可以通过配置文件来配置org.springframework.boot.autoconfigure.task.TaskExecutionProperties
类 来实现taskExecutor的自定义。
系统默认了一个TaskExecutor,我们要自定义就需要将原来的bean覆盖掉。于是我们需要如下
@Bean
@Primary
public TaskExecutor getTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(1);
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setTaskDecorator(new TaskDecorator() {
@Override
public Runnable decorate(Runnable runnable) {
return runnable;
}
});
return threadPoolTaskExecutor;
}
ThreadPoolTaskExecutor 有一属性TaskDecorator,我们可以通过实现这个来实现对任务的额外处理,比如我们需要将context传入异步线程。
ThreadPoolTaskExecutor 本来就是实现的 java中的线程池,自定义拒绝策略自然和java中的一样。
我们可能需要对某些方法指定线程池执行,即线程隔离。
因为源码中有targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
我们可以采用关联async和线程池
于是我们就有了如下代码:
@Async("doTest")
public String getToken(String appid){
System.out.println(appid);
return appid;
}
@Bean(name = "doTest")
@Primary
public TaskExecutor getTaskExecutor2(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(2);
threadPoolTaskExecutor.setMaxPoolSize(100);
threadPoolTaskExecutor.setTaskDecorator(new TaskDecorator() {
@Override
public Runnable decorate(Runnable runnable) {
return runnable;
}
});
return threadPoolTaskExecutor;
}
当async中的名称和线程池的名称相同时,就会应用到线程池
异步执行,当然也可以有返回值。
返回值类型可以有
如果返回值不为这三种的实现类,那么就不会有返回值。就直接执行返回。如果我们需要用到返回值,我们可以
public class MyListenableFuture extends ListenableFutureTask {
private T t;
public MyListenableFuture(T t){
super(new Thread(),t);
this.t = t;
}
@Override
public T get() throws InterruptedException, ExecutionException {
return t;
}
}
@Async("doTest")
public ListenableFutureTask
返回值必须为ListenableFutureTask,但是又需要new 一个,我们需要实现一个类去覆盖get方法。然后这样我们就能拿到异步的返回值了。其他类型的返回值同理。
Spring 默认的异常处理是
org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler
我们自定义异常处理,可以通过
@Bean
public AsyncConfigurer getAsyncConfig(){
return new AsyncConfigurer(){
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
dosomeThing();
System.out.println("222" + ex.getMessage() + method);
}
};
}
};
}
这样我们就能自定义异常处理了。