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 自定义 异步线程池