- 在线文档
- 项目结构
1.源码克隆:git clone https://github.com/spring-guides/gs-async-method.git 2.包含两个项目initial和complete,initial可以根据文档练习完善,complete是完整项目 3.功能描述:构建查找服务,异步查询 GitHub 用户信息并通过 GitHub 的 API 检索数据
-
源码分析
1.POM依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2.入口类 // https://blog.csdn.net/tongxin_tongmeng/article/details/128401278 @SpringBootApplication // https://blog.csdn.net/tongxin_tongmeng/article/details/128583490 @EnableAsync public class AsyncMethodApplication { public static void main(String[] args) { // close the application context to shut down the custom ExecutorService SpringApplication.run(AsyncMethodApplication.class, args).close(); } /** * 这段代码定义了一个 Executor 对象,该对象是使用 ThreadPoolTaskExecutor 类创建的线程池。Executor 是一个接口,它定义了一种方法来异步地执行任务。 * * ThreadPoolTaskExecutor 是一个实现了 Executor 接口的类,它使用线程池来执行任务。线程池是一种管理线程的机制,它提供了一种可伸缩的线程池,用于在应用程序中执行异步任务。 * * 在这段代码中,我们设置了线程池的核心线程数(corePoolSize)和最大线程数(maxPoolSize)为 2,以及队列的容量(queueCapacity)为 500。我们还为线程池中的线程设置了名称前缀(threadNamePrefix)。最后,我们调用 initialize() 方法来初始化线程池。 * * 这段代码还使用了 @Bean 注解,该注解表示此方法将会在应用程序上下文中生成一个 bean。这意味着,你可以在应用程序的其他地方使用这个 Executor 对象。 * * 例如,你可以在另一个类中注入这个 Executor 对象并使用它来异步地执行任务: * @Autowired * private Executor taskExecutor; * * public void doSomething() { * taskExecutor.execute(new Runnable() { * @Override * public void run() { * // do something * } * }); * } * @return */ @Bean public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(2); executor.setQueueCapacity(500); executor.setThreadNamePrefix("GithubLookup-"); executor.initialize(); return executor; } }
3.接口类 @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(); } /** * 这段代码定义了一个名为 findUser() 的方法,它使用 RestTemplate 发送一个 GET 请求到 GitHub API,获取指定用户的信息并返回。方法的返回值是 CompletableFuture 类型,它可以用于执行异步操作。 * * @Async 注解告诉 Spring 在单独的线程中执行 findUser() 方法。这意味着 findUser() 方法的调用将立即返回,而不等待方法实际完成。你可以在其他代码中继续执行其他任务,而无需等待 findUser() 方法的响应返回。 * * findUser() 方法返回的 CompletableFuture 对象可以用于获取方法执行的结果。例如,你可以使用 thenApply() 方法在方法执行完成后对结果进行处理: * CompletableFuture<User> future = findUser("some-user"); * future.thenApply(user -> { * // Do something with the result * return user; * }); * @param user * @return * @throws InterruptedException */ @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); } }
4.测试类 /** * 这段代码定义了一个名为 AppRunner 的组件,它实现了 CommandLineRunner 接口,因此在 Spring 应用程序启动时会执行 run() 方法。 * * AppRunner 类持有一个名为 gitHubLookupService 的 GitHubLookupService 类型的引用,并在构造函数中将其注入。GitHubLookupService 类是一个用于查找 GitHub 用户信息的服务。 * * 在 run() 方法中,代码首先记录了当前时间,然后使用 gitHubLookupService 发出三个异步请求来查找三个 GitHub 用户。请求返回的是 CompletableFuture 类型的对象,这些对象会被存储在 page1、page2 和 page3 中。 * * 接下来,代码调用 allOf() 方法,该方法接受多个 CompletableFuture 对象并返回一个新的 CompletableFuture,该 CompletableFuture 将在所有传入的 CompletableFuture 对象都完成后完成。allOf() 方法的返回值被加入,这意味着程序将在所有请求完成后再继续执行。 * * 最后,代码打印了每个请求的结果以及整个操作所用的时间。get() 方法用于获取 CompletableFuture 对象的结果。 */ @Component // https://blog.csdn.net/tongxin_tongmeng/article/details/128402169 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()); } }
- 项目演示
前两个调用发生在不同的线程 ( GithubLookup-2, GithubLookup-1) 中,第三个调用被暂停,直到两个线程之一可用为止。 要比较在没有异步功能的情况下这需要多长时间,请尝试注释掉@Async注释并再次运行该服务。总耗用时间应该会显着增加, 因为每个查询至少需要一秒钟。