【@EnableAsync】Spring Async 异步增强源码解析

1,498 阅读4分钟

0 env:

Springboot maven 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.learn</groupId>
<artifactId>webtemplate</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>webtemplate</name>
<description>Demo project for Spring Boot</description>

<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-logging</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-logging</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-log4j2</artifactId>
    </dependency>

</dependencies>

项目代码:

@SpringBootApplication
@EnableAsync
public class WebtemplateApplication {

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

	@Bean
	public Executor taskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(2);
		executor.setMaxPoolSize(2);
		executor.setQueueCapacity(500);
		executor.setThreadNamePrefix("GithubLookup-");
		executor.initialize();
		return executor;
	}
}




package com.learn.webtemplate;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown=true)
public class User {

  private String name;
  private String blog;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getBlog() {
    return blog;
  }

  public void setBlog(String blog) {
    this.blog = blog;
  }

  @Override
  public String toString() {
    return "User [name=" + name + ", blog=" + blog + "]";
  }

}




package com.learn.webtemplate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.CompletableFuture;

@Service
public class GitHubLookupService {

  private static final Logger logger = LoggerFactory.getLogger(GitHubLookupService.class);

  private final RestTemplate restTemplate;

  public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) {
    this.restTemplate = restTemplateBuilder.build();
  }

  @Async
  public CompletableFuture<User> findUser(String user) throws InterruptedException {
    logger.info("Looking up " + user);
    String url = String.format("https://api.github.com/users/%s", user);
    User results = restTemplate.getForObject(url, User.class);
    // Artificial delay of 1s for demonstration purposes
    Thread.sleep(1000L);
    return CompletableFuture.completedFuture(results);
  }

}


package com.learn.webtemplate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.CompletableFuture;

/**
 * 参考 https://spring.io/guides/gs/async-method/
 */
@Component
public class AppRunner implements CommandLineRunner {

  private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);

  private final GitHubLookupService gitHubLookupService;

  public AppRunner(GitHubLookupService gitHubLookupService) {
    this.gitHubLookupService = gitHubLookupService;
  }

  @Override
  public void run(String... args) throws Exception {
    // Start the clock
    long start = System.currentTimeMillis();

    // Kick of multiple, asynchronous lookups
    CompletableFuture<User> page1 = gitHubLookupService.findUser("PivotalSoftware");
    CompletableFuture<User> page2 = gitHubLookupService.findUser("CloudFoundry");
    CompletableFuture<User> page3 = gitHubLookupService.findUser("Spring-Projects");

    // Wait until they are all done
    CompletableFuture.allOf(page1,page2,page3).join();

    // Print results, including elapsed time
    logger.info("Elapsed time: " + (System.currentTimeMillis() - start));
    logger.info("--> " + page1.get());
    logger.info("--> " + page2.get());
    logger.info("--> " + page3.get());

  }

}

入口:

1 Spring Bean 定义入口:AsyncAnnotationBeanPostProcessor

org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors

org.springframework.scheduling.annotation.AsyncConfigurationSelector

2 工厂方法:asyncAdvisor 来初始化(org.springframework.scheduling.annotation.ProxyAsyncConfiguration#asyncAdvisor)AsyncAnnotationBeanPostProcessor

Spring bean 实例化:(org.springframework.context.annotation.internalAsyncAnnotationProcessor)

org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors

// factory method, constructor autowiring, or simple instantiation

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance // 1166 实例化: 工厂方法,构造方法,简单实例化

// @Configuration @Bean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod // 注意改 Configuration 关闭了proxyBeanMethods 即 Configuration class 类内部或者外部类调用改方法时不会做cglib增强。

// 调用工厂方法实例化

org.springframework.scheduling.annotation.ProxyAsyncConfiguration#asyncAdvisor // 完成实例化

// 开始初始化

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean // 初始化入口(后面三部曲的前置处理)

// 实现了 org.springframework.beans.factory.BeanFactoryAware 接口,初始第一步setBeanFactory 1810行

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods

// 初始化advisor

org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor#setBeanFactory // 此处扩展,完成advisor的初始化,包括确认异步线程池(默认OR自定义)

// 构造实例化advice 和 pointcut

org.springframework.scheduling.annotation.AsyncAnnotationAdvisor#AsyncAnnotationAdvisor

// 配置默认线程池

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#AsyncExecutionAspectSupport(java.util.concurrent.Executor)

// 线程池获取

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor // (默认)根据类型 TaskExecutor 或者 名字(taskExecutor)+类型(TaskExecutor)

org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor // 注意:复写了上面的方法 后面也会再次提到,这里有个印象就好(目的是没有自定义或者预置线程池时,使用org.springframework.core.task.SimpleAsyncTaskExecutor线程池进行兜底。)

// 上面就完成了 org.springframework.context.annotation.internalAsyncAnnotationProcessor 的实例化,接下来就是初始化三部曲

小结: 上面即完成了org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors 其中AsyncAnnotationBeanPostProcessor

的Bean定义和bean实例化-初始化完成过程。接下来就是,对业务bean或者bean方法异步处理的增强。

3 AsyncAnnotationBeanPostProcessor 的实际处理

入口:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization

// bean 后处理

org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization

// 织入增强具体逻辑 advice AnnotationAsyncExecutionInterceptor

小结: 完成了业务bean的增强织入逻辑,后面介绍,增强的执行;

4 入口:实际代码调用到 com.learn.webtemplate.GitHubLookupService#findUser 时,触发如下增强流程

org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed

org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke // 封装方法调用,提交到线程池中

// 线程池获取

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor

// 触发回调方法对象

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#configure // 叫钩子或者回调对象

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor // 该方法被复写方法,目的是没有自定义或者预置线程池时,使用org.springframework.core.task.SimpleAsyncTaskExecutor线程池进行兜底。

org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor // 前面提到的线程池默认获取方法

// 最后封装任务和提交,这个就比较简单了

org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit

5 线程变量传递,方案使用举例(至于如何替换,可以参考阿里ttl 或者其他开源项目)

6 自定义 异步线程池