Spring Async注解 方法异步调用 用法 源码解析 以及错误使用解决方法

18 篇文章 1 订阅

Async是Spring新加入的一种异步机制, 大多开发者也只是停留在会用的水平, 对其原理不太了解, 作为一名开发人员, 我们不仅要知其然, 更要知其所以然, 才能在项目开发过程中不会踩到不必要的坑.



用法

1.在SpringBoot启动类添加注解@EnableAsync

@EnableAsync
@SpringBootApplication
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

2.添加线程池执行器

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Bean("asyncThreadExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setKeepAliveSeconds(30);
        executor.setQueueCapacity(1000);
        return null;
    }
}

3.方法添加注解@Async

@Service
public class AsyncServiceImpl implements AsyncService {

    @Override
    @Async("asyncThreadExecutor")
    public void asyncTest() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------Async------");
    }
}

4.使用Controller测试

@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @RequestMapping("/async/test")
    public Object async() {
        asyncService.asyncTest();
        System.out.println("---------main--------");
        return "success";
    }
}

5.测试

---------main--------
-------Async------



源码解析

1.注解类EnableAsync

使用Import导入Async配置注册类
org.springframework.scheduling.annotation.EnableAsync

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
	...
	AdviceMode mode() default AdviceMode.PROXY;
	...
}

2.配置类AsyncConfigurationSelector

使用Spring ImportSelector方式向Spring中添加class为ProxyAsyncConfiguration的BeanDefinition, 后期会对其进行实例化
org.springframework.scheduling.annotation.AsyncConfigurationSelector

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

	private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";

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

}

3.代理配置类ProxyAsyncConfiguration

添加了@Configuration注解, 并注入了Bean AsyncAnnotationBeanPostProcessor
org.springframework.scheduling.annotation.ProxyAsyncConfiguration

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {

	@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对象 
		// 向容器中注入AsyncAnnotationBeanPostProcessor后置处理器对象
		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;
	}
}

4.AsyncAnnotationBeanPostProcessor

后置处理器, 父类实现了BeanPostProcessor, 内部重要方法postProcessAfterInitialization()
在这里插入图片描述AsyncAnnotationBeanPostProcessor 父类实现了BeanFactoryAware接口, 在bean初始化后会调用setBeanFactory()
org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
	...
	
	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);

		AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
		if (this.asyncAnnotationType != null) {
			advisor.setAsyncAnnotationType(this.asyncAnnotationType);
		}
		advisor.setBeanFactory(beanFactory);
		this.advisor = advisor;
	}
}

5.AsyncAnnotationAdvisor构造方法
org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#AsyncAnnotationAdvisor

public AsyncAnnotationAdvisor(
		@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

	Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
	asyncAnnotationTypes.add(Async.class);
	try {
		asyncAnnotationTypes.add((Class<? extends Annotation>)
				ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
	}
	catch (ClassNotFoundException ex) {
		// If EJB 3.1 API not present, simply ignore.
	}
	// 保存methodInterceptor 调用方法时会执行
	this.advice = buildAdvice(executor, exceptionHandler);
	this.pointcut = buildPointcut(asyncAnnotationTypes);
}

6.AnnotationAsyncExecutionInterceptor
AnnotationAsyncExecutionInterceptor 父类实现了MethodInterceptor, 后期调用代理对象方法时会执行此类中的方法 稍后分析AnnotationAsyncExecutionInterceptor.invoke()方法
org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#buildAdvice

protected Advice buildAdvice(
		@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {

	AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
	interceptor.configure(executor, exceptionHandler);
	return interceptor;
}

Spring在bean初始化后会调用此方法postProcessAfterInitialization()
org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor

public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {

	@Nullable
	protected Advisor advisor;

	protected boolean beforeExistingAdvisors = false;

	private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<>(256);

	...

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.advisor == null || bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}
		// 判断bean是否已经被代理 如果bean内有Async和Transactional注解会执行此方法
		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}
		
		// 判断bean中的方法是否有Async注解方法
		if (isEligible(bean, beanName)) {
			// 为bean添加一个简单工厂
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			// 空方法
			customizeProxyFactory(proxyFactory);
			// 创建代理
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No proxy needed.
		return bean;
	}

	protected boolean isEligible(Object bean, String beanName) {
		return isEligible(bean.getClass());
	}

	protected boolean isEligible(Class<?> targetClass) {
		Boolean eligible = this.eligibleBeans.get(targetClass);
		if (eligible != null) {
			return eligible;
		}
		if (this.advisor == null) {
			return false;
		}
		// 利用反射获取bean的各方法, 并判断是否有Async注解
		eligible = AopUtils.canApply(this.advisor, targetClass);
		this.eligibleBeans.put(targetClass, eligible);
		return eligible;
	}

	protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);
		proxyFactory.setTarget(bean);
		return proxyFactory;
	}
	...
}

org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)

public Object getProxy(@Nullable ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

先分析createAopProxy()方法, 稍后分析getProxy()方法
org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy

protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
	return getAopProxyFactory().createAopProxy(this);
}

org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			return new JdkDynamicAopProxy(config);
		}
		// 判断未实现接口 使用CGLib代理
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		// 实现了接口使用JDK代理
		return new JdkDynamicAopProxy(config);
	}
}

getProxy()方法
共实现了4个接口

com.ansheng.web.service.AsyncService
org.springframework.aop.SpringProxy
org.springframework.aop.framework.Advised
org.springframework.core.DecoratingProxy

JdkDynamicAopProxy实现了InvocationHandler, 生成代理对象, 调用目标对象的方法时, 会调用产生代理对象的invoke方法
org.springframework.aop.framework.JdkDynamicAopProxy

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	...
	
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		// 获取代理应该实现的接口
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
	
	...
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		...

		// Get the interception chain for this method.
		// 根据方法获取方法调用链, 内部使用Map存储, 提高执行效率 默认只有一个 之前提到的methodInterceptor AnnotationAsyncExecutionInterceptor对象
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		// Check whether we have any advice. If we don't, we can fallback on direct
		// reflective invocation of the target, and avoid creating a MethodInvocation.
		if (chain.isEmpty()) {
			// We can skip creating a MethodInvocation: just invoke the target directly
			// Note that the final invoker must be an InvokerInterceptor so we know it does
			// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			// We need to create a method invocation...
			MethodInvocation invocation =
					new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			// Proceed to the joinpoint through the interceptor chain.
			// 执行调用链
			retVal = invocation.proceed();
		}

		// Massage return value if necessary.
		Class<?> returnType = method.getReturnType();
		if (retVal != null && retVal == target &&
				returnType != Object.class && returnType.isInstance(proxy) &&
				!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
			// Special case: it returned "this" and the return type of the method
			// is type-compatible. Note that we can't help if the target sets
			// a reference to itself in another returned object.
			retVal = proxy;
		}
		else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
			throw new AopInvocationException(
					"Null return value from advice does not match primitive return type for: " + method);
		}
		return retVal;
		
		...
	}
	...
}

7.异步方法调用

先执行 org.springframework.aop.framework.JdkDynamicAopProxy.invoke() 方法, 然后执行AnnotationAsyncExecutionInterceptor.invoke()方法

org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor

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");
	}
	// 使用Callable创建线程
	Callable<Object> task = () -> {
		try {
			Object result = invocation.proceed();
			if (result instanceof Future) {
				return ((Future<?>) result).get();
			}
		}
		catch (ExecutionException ex) {
			handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
		}
		catch (Throwable ex) {
			handleError(ex, userDeclaredMethod, invocation.getArguments());
		}
		return null;
	};
	// 添加到线程池执行器中
	return doSubmit(task, executor, invocation.getMethod().getReturnType());
}

通过方法注解获取线程池beanName
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)) {
			// 有设置bean则根据beanName查找线程池
			targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
		}
		else {
			// 如果没有设置执行器名称, 就通过类型去寻找
			// SpringBoot默认开启了自动配置 配置文件路径
			// spring-boot-test-autoconfigure\2.3.0.RELEASE\spring-boot-test-autoconfigure-2.3.0.RELEASE.jar!\META-INF\spring.factories
			// 配置文件默认装载了bean -> org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
			// 所以如果没有在启动类上有排除, 则会使用TaskExecutionAutoConfiguration @SpringBootApplication(exclude = {TaskExecutionAutoConfiguration.class})
			// 如果设置了不状态默认执行器, 则new SimpleAsyncTaskExecutor()作为默认执行器
			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;
}

TaskExecutionAutoConfiguration

是一个配置类, 注入一个TaskExecutor, 默认配置如图
在这里插入图片描述
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration

@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {

	/**
	 * Bean name of the application {@link TaskExecutor}.
	 */
	public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

	@Bean
	@ConditionalOnMissingBean
	public TaskExecutorBuilder taskExecutorBuilder(TaskExecutionProperties properties,
			ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
			ObjectProvider<TaskDecorator> taskDecorator) {
		TaskExecutionProperties.Pool pool = properties.getPool();
		TaskExecutorBuilder builder = new TaskExecutorBuilder();
		builder = builder.queueCapacity(pool.getQueueCapacity());
		builder = builder.corePoolSize(pool.getCoreSize());
		builder = builder.maxPoolSize(pool.getMaxSize());
		builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
		builder = builder.keepAlive(pool.getKeepAlive());
		Shutdown shutdown = properties.getShutdown();
		builder = builder.awaitTermination(shutdown.isAwaitTermination());
		builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
		builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
		builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);
		builder = builder.taskDecorator(taskDecorator.getIfUnique());
		return builder;
	}

	@Lazy
	@Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
			AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
	@ConditionalOnMissingBean(Executor.class)
	public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
		return builder.build();
	}

}

doSubmit() 方法如果返回类型为CompletableFuture / ListenableFuture / Future三种类型作为返回类型时, 会在执行完成后返回结果, 否则直接异步执行并返回null
org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit

protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
	if (CompletableFuture.class.isAssignableFrom(returnType)) {
		return CompletableFuture.supplyAsync(() -> {
			try {
				return task.call();
			}
			catch (Throwable ex) {
				throw new CompletionException(ex);
			}
		}, executor);
	}
	else if (ListenableFuture.class.isAssignableFrom(returnType)) {
		return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
	}
	else if (Future.class.isAssignableFrom(returnType)) {
		return executor.submit(task);
	}
	else {
		executor.submit(task);
		return null;
	}
}



总结

Async 通过使用Spring的ImportSelector方法, 向Spring中注册了一个Configuration配置类, 并且注入了一个BeanPostProcessor, 在bean实例化后调用postProcessAfterInitialization方法, 为对象产生动态代理(如果目标类实现了接口, 默认使用JDK代理, 否则使用CGLib)

执行异步方法时, 调用JdkDynamicAopProxy.invoke()方法, 获取调用链并执行AnnotationAsyncExecutionInterceptor.invoke()方法, 然后根据方法注解获取线程池执行器, 创建并submit线程,

如果有Feture类型返回对象, 等执行完成后返回结果, 否则异步调用并直接返回null



错误使用

类内部直接调用不会生效

使用Async的类,如果产生循环依赖(A->A; A->B, B->A)无法解决

原因
由于Async使用动态代理实现, 所以类内部直接调用没有使用动态代理, 所以不会生效, 这一点和@Transactional注解原理类似,但又不完全一样

@Async产生代理的时机是在bean初始化完成后AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization方法内实现 @Transactional是在实例化之后立即被代理,其他对象可以直接引用到当前对象的早期依赖.

解决方法

1.类内部注入自己,添加@Lazy注解

@Lazy
@Autowired
private AsyncService asyncService;

2.通过实现BeanFactoryAware结果, 获取到BeanFactory后, 从容器中再次获取代理对象, 然后调用方法

@Service
public class AsyncServiceImpl implements AsyncService, ApplicationContextAware {

    private ApplicationContext ac;

    @Override
    @Async("asyncExecutor")
    public void processAsync(int index) {
        ...
    }

    @Override
    public void inLazy(int index) {
        ac.getBean(AsyncService.class).processAsync(2);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ac = applicationContext;
    }
}

3.将方法移到其他类中, 通过注入bean完成调用

4.不用默认代理模式,使用Aspectj加载时织入完成异步
引入包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

启动类指定mode AspectJ

@EnableAsync(mode = AdviceMode.ASPECTJ)

添加启动参数 指定agent
-javaagent:C:\Users\GuanDS.m2\repository\org\aspectj\aspectjweaver\1.9.2\aspectjweaver-1.9.2.jar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值