带着问题看源码二
问题现象
直接调用同一个类中的异步方法,没有出现异步效果
public void log2() {
System.out.println("-----log2:"+Thread.currentThread().getName());
log3();
}
@Async
public void log3() {
System.out.println("-----log3:"+Thread.currentThread().getName());
}
log3并没有被异步调用,@Async注解失效。
解决方法
使用service调用异步方法,出现异步效果
@Resource
@Lazy
private AsyncMethod asyncMethod;
public void log2() {
System.out.println("-----log2:"+Thread.currentThread().getName());
asyncMethod.log3();
}
@Async
public void log3() {
System.out.println("-----log3:"+Thread.currentThread().getName());
}
追踪源码
初始化
1、@EnableAsync注解引入了AsyncConfigurationSelector
@Import(AsyncConfigurationSelector.class)
2、注册AsyncAnnotationBeanPostProcessor
@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 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;
}
}
3、将目标类的bean改为代理对象
利用Bean的后置处理器将目标类bean改为代理对象
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
、、、
//如果bean中的方法需要异步处理,给bean构造代理包装对象
if (isEligible(bean, beanName)) {
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;
}
异步实现
正确使用代理时,asyncMethod.log3(),asyncMethod已经不是原始的bean,而是原始bean的代理包装对象。
1、代理类获取拦截器调用链拿到AnnotationAsyncExecutionInterceptor
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//这里的method,就是log3()方法的包装对象
、、、
try {
、、、
//拿到拦截器调用链,获取AnnotationAsyncExecutionInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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 = methodProxy.invoke(target, argsToUse);
}
else {
//调用拦截器方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
、、、、
}
}
2、利用拦截器异步调用目标方法
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<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());
}
原因分析
经过上面的源码追溯我们已经清楚的知道了,要想使用异步任务必须要使其代理包装对象正常工作,直接调用方法无法触发代理对象方法调用,必须指明对象来调用方法。