一、使用,基于Maven工程

1.1、pom.xml

没有特殊的依赖,common里面也就lombok,guava等基础包

<dependencies>
        <dependency>
            <groupId>com.tonels</groupId>
            <artifactId>common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot-starter-web.version}</version>
        </dependency>

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

    </dependencies>

1.2、定义异步操作,配置,两个文件

异步配置

@Configuration
@EnableAsync
public class AsyncConfiguration 
{
   
	@Bean(name = "asyncExecutor")
	public Executor asyncExecutor() {
   
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(3);
		executor.setMaxPoolSize(3);
		executor.setQueueCapacity(100);
		executor.setThreadNamePrefix("AsynchThread-");
		executor.initialize();
		return executor;
	}
}

异步操作

@Service
public class AsyncService {
   

	private static Logger log = LoggerFactory.getLogger(AsyncService.class);

	@Autowired
	private RestTemplate restTemplate;

	@Bean
	public RestTemplate restTemplate() {
   
		return new RestTemplate();
	}

	@Async("asyncExecutor")
	public CompletableFuture<EmployeeNames> getEmployeeName() throws InterruptedException {
   
		log.info("获取名字 Starts");
		EmployeeNames employeeNameData = restTemplate.getForObject("http://localhost:1210/names", EmployeeNames.class);

		log.info("employeeNameData, {}", employeeNameData);
		Thread.sleep(1000L);	// 延迟 1 秒
		log.info("获取名字 结束");
		return CompletableFuture.completedFuture(employeeNameData);
	}

	@Async("asyncExecutor")
	public CompletableFuture<EmployeeAddresses> getEmployeeAddress() throws InterruptedException {
   
		log.info("获取地址 Starts");
		EmployeeAddresses employeeAddressData = restTemplate.getForObject("http://localhost:1210/addresses", EmployeeAddresses.class);

		log.info("地址数据, {}", employeeAddressData);
		Thread.sleep(1000L);	// 延迟一秒
		log.info("获取地址 结束");
		return CompletableFuture.completedFuture(employeeAddressData);
	}

	@Async("asyncExecutor")
	public CompletableFuture<EmployeePhone> getEmployeePhone() throws InterruptedException {
   
		log.info("获取电话 Starts");
		EmployeePhone employeePhoneData = restTemplate.getForObject("http://localhost:1210/phones", EmployeePhone.class);

		log.info("电话数据, {}", employeePhoneData);
		Thread.sleep(1000L);	// 延迟一秒
		log.info("获取电话 结束");
		return CompletableFuture.completedFuture(employeePhoneData);
	}

}

1.3、测试Controller,两个文件

  • EmployeeDataController ,一般的接口Controller定义,根据需要,定义在异步操作中

  • AsyncController ,用于测试异步接口操作的

  • EmployeeDataController

@RestController
public class EmployeeDataController {
   

    private static Logger log = LoggerFactory.getLogger(EmployeeDataController.class);

    @GetMapping(value = "/addresses")
    public EmployeeAddresses getAddresses() {
   
        EmployeeAddresses employeeAddressesList = new EmployeeAddresses();

        EmployeeAddress e1 = new EmployeeAddress().setHouseNo("1111").setStreetNo("111").setZipCode("111111");
        EmployeeAddress e2 = new EmployeeAddress().setHouseNo("1111").setStreetNo("111").setZipCode("111111");

        ArrayList<EmployeeAddress> list = Lists.newArrayList(e1, e2);

        employeeAddressesList.setEmployeeAddressList(list);

        return employeeAddressesList;
    }

    @GetMapping(value = "/phones")
    public EmployeePhone getPhoneNumbers() {
   
        EmployeePhone employeePhone = new EmployeePhone();

        ArrayList<String> list = Lists.newArrayList("100000", "200000");

        employeePhone.setPhoneNumbers(list);

        return employeePhone;
    }

    @GetMapping(value = "/names")
    public EmployeeNames getEmployeeName() {
   
        EmployeeNames employeeNamesList = new EmployeeNames();

        EmployeeName e1 = new EmployeeName().setFirstName("zhang").setLastName("san");
        EmployeeName e2 = new EmployeeName().setFirstName("wang").setLastName("yi");

        List<EmployeeName> list = Lists.newArrayList(e1, e2);

        employeeNamesList.setEmployeeNameList(list);

        // int a = 5/0;
        return employeeNamesList;
    }

    @GetMapping("/trade")
    public void t1(Trade trade) {
   

        System.out.println(trade.getAmount());
        System.out.println(trade.getTradeDate());
    }
}
  • AsyncController
@RestController
public class AsyncController {
   

	private static Logger log = LoggerFactory.getLogger(AsyncController.class);

	@Autowired
	private AsyncService service;

	@GetMapping("/testAsynch")
	public void testAsynch() throws InterruptedException, ExecutionException {
   
		log.info("测试开始");

		CompletableFuture<EmployeeAddresses> employeeAddress = service.getEmployeeAddress();
		CompletableFuture<EmployeeNames> employeeName = service.getEmployeeName();
		CompletableFuture<EmployeePhone> employeePhone = service.getEmployeePhone();

		CompletableFuture.allOf(employeeAddress, employeeName, employeePhone).join();
		
		log.info("雇员地址--> " + employeeAddress.get());
		log.info("雇员名字--> " + employeeName.get());
		log.info("雇员电话--> " + employeePhone.get());
	}
}

还有Model的定义,不是很重要,主要是流程的测试

1.4、启动

@SpringBootApplication
public class SpringBootAsyncApplication {
   

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

1.5、测试

项目启动后,访问localhost:8080/testAsynch,可发现三个接口是异步同时调用的,

二、思考及应用场景

2.1、多个同时异步接口调用时,怎么保证事务的基本操作?

2.2、使用场景:包括日志处理,消息,邮件推送等

三、源码地址,Github地址链接