AsyncExecutionInterceptor
终于,从此处开始。可议看出它是一个MethodInterceptor
,是一个增强器了。但是从命名中也可以看出,它已经能够处理异步的执行了(比如基于XML方式的),但是还和注解无关
// 他继承自AsyncExecutionAspectSupport 来处理异步方法的处理,同时是个MethodInterceptor,来增强复合条件的方法 public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { ... // 显然这个方法它直接返回null,因为XML配置嘛~~~~ @Override @Nullable protected String getExecutorQualifier(Method method) { return null; } // 这个厉害了。如果父类返回的defaultExecutor 为null,那就new一个SimpleAsyncTaskExecutor作为默认的执行器 @Override @Nullable protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) { Executor defaultExecutor = super.getDefaultExecutor(beanFactory); return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); } // 最高优先级 希望的是最优先执行(XML配置就是这种优先级) @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // 最重要的当然是这个invoke方法 @Override @Nullable public Object invoke(final MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // 注意:此处是getMostSpecificMethod 拿到最终要执行的那个方法 Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); // 桥接方法~~~~~~~~~~~~~~ final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // determine一个用于执行此方法的异步执行器 AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod); if (executor == null) { throw new IllegalStateException( "No executor specified and no default executor set on AsyncExecutionInterceptor either"); } // 构造一个任务,Callable(此处不采用Runable,因为需要返回值) Callable<Object> task = () -> { try { // result就是返回值 Object result = invocation.proceed(); // 注意此处的处理~~~~ 相当于如果不是Future类型,就返回null了 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; }; // 提交任务~~~~invocation.getMethod().getReturnType()为返回值类型 return doSubmit(task, executor, invocation.getMethod().getReturnType()); } }
SimpleAsyncTaskExecutor:异步执行用户任务的SimpleAsyncTaskExecutor。每次执行客户提交给它的任务时,它会启动新的线程,并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用。默认时,concurrencyLimit取值为-1,即**不启用**资源节流
所以它不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程(因此建议我们在使用@Aysnc的时候,自己配置一个线程池,节约资源)
AnnotationAsyncExecutionInterceptor
很显然,他是在AsyncExecutionInterceptor的基础上,提供了对@Async注解的支持。所以它是继承它的。
// 它继承自AsyncExecutionInterceptor ,只复写了一个方法 public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor { ... // 由此可以见它就是去拿到@Async的value值。以方法的为准,其次是类上面的 // 备注:发现这里是不支持EJB的@Asynchronous注解的,它是不能指定执行器的 @Override @Nullable protected String getExecutorQualifier(Method method) { // Maintainer's note: changes made here should also be made in // AnnotationAsyncExecutionAspect#getExecutorQualifier Async async = AnnotatedElementUtils.findMergedAnnotation(method, Async.class); if (async == null) { async = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class); } return (async != null ? async.value() : null); } }
@Async注解失效得原因
如下:若我是这么写的
@Service public class HelloServiceImpl implements HelloService { @Override public Object hello() { System.out.println("当前线程:" + Thread.currentThread().getName()); helloPrivate(); // 同类内部方法调用 return "service hello"; } @Async @Override public Object hello2() { System.out.println("当前线程:" + Thread.currentThread().getName()); return null; } }
最终输出为;
当前线程:http-nio-8080-exec-3 当前线程:http-nio-8080-exec-3
发现都是tomcat的线程。what?@Async
异步线程木有生效????
这个也不是什么新问题了。在之前一篇博文:【小家java】Spring事务不生效的原因大解读
原因也是一样的,都是所谓的:代理失效
问题。
如何解决?这里就说一种方案吧:
@Service public class HelloServiceImpl implements HelloService { @Autowired private ApplicationContext applicationContext; @Override public Object hello() { System.out.println("当前线程:" + Thread.currentThread().getName()); // 从容器里拿到该类型的实例~~~~(注意:若是JDK代理,这里的类型只能传接口,而不能是实现类,否则NoSuchBean...) HelloService helloService = applicationContext.getBean(HelloService.class); // 然后用本实例去调用 而不是用默认的this去调用 helloService.hello2(); return "service hello"; } @Async @Override public Object hello2() { System.out.println("当前线程:" + Thread.currentThread().getName()); return null; } }
输出:
当前线程:http-nio-8080-exec-4 当前线程:SimpleAsyncTaskExecutor-2
显然这才是我们想要的结果。因此在同类调用的时候,一定要特别的小心,很有可能是不生效的。
各位可以看看你们同事写的代码,据我推测,肯定有同事写了这种
不生效的代码
~~
使用推荐配置
@EnableAsync //对应的@Enable注解,最好写在属于自己的配置文件上,保持内聚性 @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); //核心线程数 executor.setMaxPoolSize(20); //最大线程数 executor.setQueueCapacity(1000); //队列大小 executor.setKeepAliveSeconds(300); //线程最大空闲时间 executor.setThreadNamePrefix("fsx-Executor-"); 指定用于新创建的线程名称的前缀。 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略(一共四种,此处省略) // 这一步千万不能忘了,否则报错: java.lang.IllegalStateException: ThreadPoolTaskExecutor not initialized executor.initialize(); return executor; } // 异常处理器:当然你也可以自定义的,这里我就这么简单写了~~~ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new SimpleAsyncUncaughtExceptionHandler(); } }
使用Spring的异步,我个人十分建议配置上自己自定义的配置器(如上配置仅供参考),这样能够更好的掌握(比如异常处理AsyncUncaughtExceptionHandler~~~)
总结
有了Spring AOP整体运行原理作为基础,再看这些基于AOP的应用,简直行云流水。因此还是应正了那句话:你能走多远,就看你的根基有多稳。
最后,使用的有两个建议:
- 异步方法建议尽量处理耗时的任务,或者是处理不希望阻断主流程的任务(比如异步记录操作日志)
- 尽量为@Async准备一个专门的线程池来提高效率减少开销,而不要用Spring默认的SimpleAsyncTaskExecutor,它不是一个真正的线程池~