我的心血全在这了,这种方式讲@Async原理

2.1.4 定义通知

protected Advice buildAdvice(

@Nullable Supplier executor, @Nullable Supplier exceptionHandler) {

// 定义通知

AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);

interceptor.configure(executor, exceptionHandler);

return interceptor;

}

通知就是最终要执行的,也是相当重要的一部分,既然很重要,那就需要我们来看看具体的实现

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 task = () -> {

try {

Object result = invocation.proceed();

if (result instanceof Future) {

return ((Future<?>) result).get();

}

}

return null;

};

return doSubmit(task, executor, invocation.getMethod().getReturnType());

}

protected Object doSubmit(Callable task, AsyncTaskExecutor executor, Class<?> returnType) {

// 异步任务的返回值类型是CompletableFuture

if (CompletableFuture.class.isAssignableFrom(returnType)) {

return CompletableFuture.supplyAsync(() -> {

try {

return task.call();

}

catch (Throwable ex) {

throw new CompletionException(ex);

}

}, executor);

}

// 异步任务的返回值类型是ListenableFuture

else if (ListenableFuture.class.isAssignableFrom(returnType)) {

return ((AsyncListenableTaskExecutor) executor).submitListenable(task);

}

// 异步任务的返回值类型是Future

else if (Future.class.isAssignableFrom(returnType)) {

return executor.submit(task);

}

// 否则交由线程池来处理,没有返回值

else {

executor.submit(task);

return null;

}

}

通知的具体实现如下:

  • 第一步获取异步任务线程池,用来执行异步任务

  • 使用Callable包裹目标方法

  • 执行异步异步任务,根据不同的返回值类型做相应的处理

通过通知可以了解到异步任务最终实现的原理,你可能还有疑问,那就是如何告知通知来执行异步任务呢?

不知道,你是否还记得上文提到的BeanPostProcessor接口,下面就来看看它的具体实现

2.1.3 BeanPostProcessor实现

提到BeanPostProcessor接口,你就应该立刻意识到它的处理方法肯定对Bean做了某些处理,比如生成代理

有了基础的意识后就来看看此处对应的后置处理实现

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) {

// 判断当前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;

}

通过BeanPostProcessor的后置处理对满足切点的Bean生成代理,在调用目标方法的时候,会执行通知的invoke()方法

到此,异步实现原理部分就结束了,其实原理很简单。我们需要做的就是定义切点、通知;要想实现对目标方法的增强,自然而然想到的就是反向代理;最后就是如何对原有的Bean进行改变呢?此刻就需要联系到与Bean生命周期相关的BeanPostProcessor接口

2.2 线程池使用

线程池这部分还是相当重要的,使用不当可能会导致意向不到的问题发生,比如内存溢出、无限制创建线程、业务之间相互影响等等

*

By default, Spring will be searching for an associated thread pool definition: * either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context, * or an {@link java.util.concurrent.Executor} bean named “taskExecutor” otherwise. 复制代码

根据官方文档的说明,可以得知Spring会从上下文中获取唯一的TaskExecutor或者名称"taskExecutor"的Bean作为线程池,默认的线程池在TaskExecutionAutoConfiguration自动配置类中定义,默认的线程池相关配置如下

我的心血全在这了,这种方式讲@Async原理,你别再不懂Spring了

可以看到默认线程池的队列大小和最大线程数都是Integer的最大值,显然会给系统留下一定的风险隐患,因此我们需要针对每个异步任务自定义线程池,然后在@Async()注解上指明对应线程池的Bean名称

2.3 异常处理

异步任务的异常处理默认情况下只会打印日志信息,不会做任何额外的额处理,官方文档也有相关的说明

Besides, annotated methods having a * {@code void} return type cannot transmit any exception back to the caller. By default, * such uncaught exceptions are only logged. 复制代码

SimpleAsyncUncaughtExceptionHandler就是异步任务异常处理的默认实现,如果想要自定义异常处理,只需要AsyncConfigurer接口即可

2.4 返回值类型

关于返回值类型,首先来看下官方的相关说明

*

In terms of target method signatures, any parameter types are supported. * However, the return type is constrained to either {@code void} or * {@link java.util.concurrent.Future}. In the latter case, you may declare the * more specific {@link org.springframework.util.concurrent.ListenableFuture} or * {@link java.util.concurrent.CompletableFuture} types which allow for richer * interaction with the asynchronous task and for immediate composition with * further processing steps. 复制代码

从官方说明上可以看出返回值类型只支持4种类型:

  • void

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。

Mybatis源码解析

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-VF8PESBL-1710422889498)]

总结

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。

Mybatis源码解析

[外链图片转存中…(img-LSEIEbWL-1710422889499)]

[外链图片转存中…(img-Zm399WmV-1710422889499)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值