Spring异步处理Async

Spring异步处理Async

Spring中需要方法异步执行的时候就会用到@Async注解,使用此注解时可以将该函数交予线程池异步执行。

我们首先需要在启动类上加上EnableAsync注解

@SpringBootApplication
@EnableAsync
public class PaymentApplication {

    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(PaymentApplication.class);
        springApplication.run(args);
    }

}

EnableAsync

源码为

@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; }

EnableAsync是如何被Spring扫描到的?

首先,方法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的解析流程

enableAsync也是解析的import来实现其功能的,先通过org.springframework.context.annotation.ConfigurationClassParser#getImports得到所有import得类,然后执行import中的函数org.springframework.context.annotation.ConfigurationClassParser#processImports,

在import中可以有3中方式执行注入

  1. 继承org.springframework.context.annotation.ImportSelector接口
  2. 继承org.springframework.context.annotation.ImportBeanDefinitionRegistrar接口
  3. 普通类,将被当作config类,就相当于加入@Configuration 注解,里面的类将会直接执行

enableAsync就是采用的第一种方法执行的。

,这个方法的结果是将org.springframework.scheduling.annotation.ProxyAsyncConfiguration返回,

然后将ProxyAsyncConfiguration,执行processImports的第三种注入方式。然后最终注入org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor类。

AsyncAnnotationBeanPostProcessor是一个切面的processor。

AsyncAnnotationBeanPostProcessor的执行流程

我们得到AsyncAnnotationBeanPostProcessor,这个类被注入容器。

因为AsyncAnnotationBeanPostProcessor继承了BeanFactoryAware,在注入bean的时候将会执行org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods方法,设置beanFactory,在beanFactory的时候,将会执行构造org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(java.util.function.Supplier, 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执行

Async中的线程池配置

在执行异步任务中,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默认的线程池?

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中的名称和线程池的名称相同时,就会应用到线程池

方法中获取异步方法的返回值

异步执行,当然也可以有返回值。

返回值类型可以有

  1. CompletableFuture
  2. ListenableFuture
  3. Future

如果返回值不为这三种的实现类,那么就不会有返回值。就直接执行返回。如果我们需要用到返回值,我们可以

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 getToken(String appid) throws InterruptedException {
        Thread.sleep(5000L);
        return new MyListenableFuture<>(appid);
    }
    public Persono hello(@RequestParam(value = "test",required = false)String test,@CookieValue(value = "name",required = false)String name,
        @PathVariable("map") String map) throws InterruptedException, ExecutionException {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
        Future result = myTest.getToken(map);
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
        Thread.sleep(5000L);
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
        Object o = result.get();
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
        return new Persono((String)o,123);
    }
 
  

返回值必须为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);
                    }
                };
            }
        };
    }

这样我们就能自定义异常处理了。

你可能感兴趣的:(Spring,spring,java,servlet)