【Spring 技术实战】@Async 机制的使用技巧以及异步注解源码解析(1)

总结

就写到这了,也算是给这段时间的面试做一个总结,查漏补缺,祝自己好运吧,也希望正在求职或者打算跳槽的 程序员看到这个文章能有一点点帮助或收获,我就心满意足了。多思考,多问为什么。希望小伙伴们早点收到满意的offer! 越努力越幸运!

金九银十已经过了,就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。但很多小伙伴却苦于没有合适的资料来回顾整个 Java 知识体系,或者有的小伙伴可能都不知道该从哪里开始复习。我偶然得到一份整理的资料,不论是从整个 Java 知识体系,还是从面试的角度来看,都是一份含技术量很高的资料。

三面蚂蚁核心金融部,Java开发岗(缓存+一致性哈希+分布式)

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

需要这份系统化的资料的朋友,可以点击这里获取

If only one item needs to be customized, null can be returned to keep the default settings. Consider also extending from AsyncConfigurerSupport when possible.Note: In the above example the ThreadPoolTaskExecutor is not a fully managed Spring bean. Add the @Bean annotation to the getAsyncExecutor() method if you want a fully managed bean. In such circumstances it is no longer necessary to manually call the executor.initialize() method as this will be invoked automatically when the bean is initialized.

同比相关

<beans> <task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/> <task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/> <bean id="asyncBean" class="com.foo.MyAsyncBean"/> <bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/> </beans>

重点说明

The mode() attribute controls how advice is applied: If the mode is AdviceMode.PROXY (the default), then the other attributes control the behavior of the proxying. 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.

  • 这里就说明了 @Async 必须在不同方法中调用。

Note that if the mode() is set to AdviceMode.ASPECTJ, then the value of the proxyTargetClass() attribute will be ignored. Note also that in this case the spring-aspects module JAR must be present on the classpath, with compile-time weaving or load-time weaving applying the aspect to the affected classes. There is no proxy involved in such a scenario; local calls will be intercepted as well.//当然也可以用 Aspect 模式织入(需要引入 spring-aspects 模块需要的 jar)

功能解析

@Async

  • 该注解可以标记一个异步执行的方法,也可以用来标注类,表示类中的所有方法都是异步执行的。

  • 入参随意,但返回值只能是 void 或者 Future.(ListenableFuture 接口/CompletableFuture 类)

  • Future 是代理返回的切实的异步返回,用以追踪异步方法的返回值。当然也可以使用 AsyncResult 类(实现 ListenableFuture 接口)(Spring 或者 EJB 都有)或者 CompletableFuture 类

  • 加在类上表示整个类都使用,加在方法上会覆盖类上的设置

  • value 字段用以限定执行方法的执行器名称(自定义):Executor 或者 TaskExecutor

@EnableAsync

  • 开启 spring 异步执行器,类似 xml 中的 task 标签配置,需要联合 @Configuration 注解一起使用

  • 默认情况下 spring 会先搜索 TaskExecutor 类型的 bean 或者名字为 taskExecutor 的 Executor 类型的 bean,都不存在使用 SimpleAsyncTaskExecutor 执行器

  • 针对于上面章节的【覆盖定制化 UncaughtExceptionHandler/getAsyncExecutor】可实现 AsyncConfigurer 接口复写 getAsyncExecutor 获取异步执行器,getAsyncUncaughtExceptionHandler 获取异步未捕获异常处理器

  • @Configuration

  • 注解类和 xml 基本一致,但是使用注解类还可以自定义线程名前缀(上面的 AppConfig-》getAsyncExecutor-》setThreadNamePrefix)

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {`

/**该属性用来支持用户自定义异步注解,默认扫描spring的@Async和EJB3.1的@code @javax.ejb.Asynchronous * Indicate the 'async' annotation type to be detected at either class * or method level. * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1 * {@code @javax.ejb.Asynchronous} annotation will be detected. * <p>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;

/**标明是否需要创建CGLIB子类代理,AdviceMode=PROXY时才适用。注意设置为true时,其它spring管理的bean也会升级到CGLIB子类代理 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed * to standard Java interface-based proxies. * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>. * <p>The default is {@code false}. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * 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 &mdash; for example, in tests. */ boolean proxyTargetClass() default false;

/**标明异步通知将会如何实现,默认PROXY,如需支持同一个类中非异步方法调用另一个异步方法,需要设置为ASPECTJ * Indicate how async advice should be applied. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * 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;

/**标明异步注解bean处理器应该遵循的执行顺序,默认最低的优先级(Integer.MAX_VALUE,值越小优先级越高) * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor} * should be applied. * <p>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;``}

执行流程

核心注解就是 @Import(AsyncConfigurationSelector.class),就是属于 ImportSelector 接口的 selectImports()方法,源码如下:

查询器:基于 @EnableAsync 中定义的模式 AdviceMode 加在 @Configuration 标记的类上,确定抽象异步配置类的实现类

/** * Selects which implementation of {@link AbstractAsyncConfiguration} should be used based * on the value of {@link EnableAsync#mode} on the importing {@code @Configuration} class. * * @author Chris Beams * @since 3.1 * @see EnableAsync * @see ProxyAsyncConfiguration */``public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

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

/** * {@inheritDoc} * @return {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration} for * {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()}, respectively */ @Override public String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY://如果配置的PROXY,使用ProxyAsyncConfiguration return new String[] { ProxyAsyncConfiguration.class.getName() }; case ASPECTJ://如果配置的ASPECTJ,使用ProxyAsyncConfiguration return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME }; default: return null; } }``}

JDK 接口代理-选一个类 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 bpp = new AsyncAnnotationBeanPostProcessor(); Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation"); if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) { bpp.setAsyncAnnotationType(customAsyncAnnotation); } if (this.executor != null) { bpp.setExecutor(this.executor); } if (this.exceptionHandler != null) { bpp.setExceptionHandler(this.exceptionHandler); } bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass")); bpp.setOrder(this.enableAsync.<Integer>getNumber("order")); return bpp; }``}

  • 如果 @EnableAsync 中用户自定义了 annotation 属性,即异步注解类型

  • 新建一个异步注解 bean 后处理器

  • 设置 Executor:设置线程任务执行器

  • AsyncUncaughtExceptionHandler:设置异常处理器

  • 设置是否升级到 CGLIB 子类代理,默认不开启

  • 设置执行优先级,默认最后执行

ProxyAsyncConfiguration 就两点:

  1. 就是继承了 AbstractAsyncConfiguration

  2. 定义了一个 bean:AsyncAnnotationBeanPostProcessor

AbstractAsyncConfiguration

/** * Abstract base {@code Configuration} class providing common structure for enabling * Spring's asynchronous method execution capability. * 抽象异步配置类,封装了通用结构,用以支持spring的异步方法执行能力 * @author Chris Beams * @author Stephane Nicoll * @since 3.1 * @see EnableAsync */``@Configuration``public abstract class AbstractAsyncConfiguration implements ImportAware {

protected AnnotationAttributes enableAsync;//enableAsync的注解属性

protected Executor executor;//Doug Lea老李头设计的线程任务执行器

protected AsyncUncaughtExceptionHandler exceptionHandler;//异常处理器

@Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableAsync = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false)); if (this.enableAsync == null) { throw new IllegalArgumentException( "@EnableAsync is not present on importing class " + importMetadata.getClassName()); } }

/** * Collect any {@link AsyncConfigurer} beans through autowiring. */ @Autowired(required = false) void setConfigurers(Collection<AsyncConfigurer> configurers) { if (CollectionUtils.isEmpty(configurers)) { return; } if (configurers.size() > 1) { throw new IllegalStateException("Only one AsyncConfigurer may exist"); } AsyncConfigurer configurer = configurers.iterator().next(); this.executor = configurer.getAsyncExecutor(); this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler(); }``}

主要设置为一下三点

  • 1)注解属性

  • 2)异步任务执行器

  • 3)异常处理器

方法:

  • 1)setImportMetadata 设置注解属性

  • 2)setConfigurers 设置异步任务执行器和异常处理器

AsyncAnnotationBeanPostProcessor

AsyncAnnotationBeanPostProcessor 这个类的 Bean 初始化时 : BeanFactoryAware 接口 setBeanFactory 方法中,对 AsyncAnnotationAdvisor 异步注解切面进行了构造。

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

AsyncAnnotationAdvisor:

通过 AsyncAnnotationAdvisor 建立异步化的 AOP 切面拦截操作处理机制

  • AsyncAnnotationBeanPostProcessor -> postProcessAfterInitialization()

  • 具体的后置处理:AsyncAnnotationBeanPostProcessor 的后置 bean 处理是通过其父类 AbstractAdvisingBeanPostProcessor 来实现的,

  • 该类实现了 BeanPostProcessor 接口,复写 postProcessAfterInitialization 方法

总结

面试建议是,一定要自信,敢于表达,面试的时候我们对知识的掌握有时候很难面面俱到,把自己的思路说出来,而不是直接告诉面试官自己不懂,这也是可以加分的。

以上就是蚂蚁技术四面和HR面试题目,以下最新总结的最全,范围包含最全MySQL、Spring、Redis、JVM等最全面试题和答案,仅用于参考

一份还热乎的蚂蚁金服面经(已拿Offer)面试流程4轮技术面+1轮HR

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

需要这份系统化的资料的朋友,可以点击这里获取

总结的最全**,范围包含最全MySQL、Spring、Redis、JVM等最全面试题和答案,仅用于参考

[外链图片转存中…(img-jQjmInCl-1715481852060)]

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

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值