SpringBoot-异步任务

什么是异步任务?

无需等待任务执行完成就可以进行下一步的任务。

为什么要有异步任务?

某些任务的执行与业务逻辑无关,此类任务若同步执行会增加业务的整体处理时间,所以需要异步处理该任务。例如:12306买票。购票与短信提醒没有任何关联,即使短信发送失败,购票操作也可以成功。

如何实现异步任务
  1. Java提供的多线程处理
  2. SpringBoot提供异步任务的封装
SpringBoot实现异步任务
  • @EnableAsync 异步任务的开关
  • @Async 标记方法为异步方法

具体操作

  • 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
  • 撸代码
@EnableAsync //启动类加入该注解开启异步任务
@RestController
@SpringBootApplication
public class App {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(App.class, args);
    }

}

// 票务系统
@RestController
public class TicketController {

    @Autowired
    private SMS sms;

    @RequestMapping("/buy")
    public String buy() {
        long s = System.currentTimeMillis();
        // 购票逻辑省略
        // 购票操作完成后,调用短信接口异步发送购票信息
        sms.send();
        long e = System.currentTimeMillis();
        return String.format("购票成功,耗时:%f秒", (e - s) / 1000.0);
    }
}

// 短信服务-Short Messaging Service
@Service
public class SMS {

    @Async // 标记为异步方法
    public void send() {
        try {
            // 假设调用短信系统耗时3秒
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 输出结果

    浏览区输入:localhost:8080 得到结果

    购票成功,耗时:0.003000秒

  • 注意

    通过new创建的对象或调用本类的方法,@Async不会生效,只有容器内bean的方法才会有功能性的增强。

源码解析
  • 阅读源码必然从异步任务的开关@EnableAsync看起
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    // 具体属性省略
}

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {

    // 省略无关内容

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

可以看到在@EnableAsync注解上标注了@Import(AsyncConfigurationSelector.class)这样一行代码。所以AsyncConfigurationSelector的代码也顺便贴在上面。

@Import注解的作用是向容器注册组件。在博客中有详细介绍,不懂的同学可以点进去看看。

通过查看AsyncConfigurationSelector的源码,我们发现默认配置的情况下,@EnableAsync会通过@Inport注解向容器注册ProxyAsyncConfiguration的实例。

  • 所以,接下来看看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() {
        // 内容省略
    }
}

通过查看源码,AbstractAsyncConfiguration向容器中注册了AsyncAnnotationBeanPostProcessor的实例,通过类名也能推测出该类的作用:标记了@Async注解的Bean的后置处理器。

后置处理器:Spring暴露出来的拓展点,对容器中产生的Bean进行改造,使其拥有我们配置的功能。

avatar

通过UML图,知道AsyncAnnotationBeanPostProcessor通过实现BeanPostProcessor接口得到了对Bean后置处理的功能,并且通过继承了ProxyProcessorSupport类得到了对bean进行代理处理的功能。

也就是异步方法的实现,是通过将容器中bean组件进行拦截处理,通过代理对象的方式得到了异步处理任务的功能。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页