1.异步调用简介 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步一步执行,每一步必须等到上一步执行完成之后才能执行,而异步调用则无需等待上一步程序执行完成即可执行。在日常开发的项目中,当访问的接口较慢或者做耗时任务时,避免程序一直卡在耗时任务上,使程序能够并行执行,可以使用多线程来并行处理,也可以使用SpringBoot提供的异步处理方式@Async来处理。
2.引入依赖 新建一个SpringBoot工程,并在pom.xml文件中添加需要的dependency。
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency>3.添加配置 在application.properties文件中添加如下配置信息。
############################################################ # # MySQL配置 # ############################################################ ### 连接信息 spring.datasource.url = jdbc:mysql://localhost:3306/mydb ### 用户名 spring.datasource.username = root ### 密码 spring.datasource.password = admin123 ### 驱动 spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver ############################################################ # # MyBatis配置 # ############################################################ ### po类存放目录 mybatis.type-aliases-package = com.leichuangkj.async.dao.po ### mapper(.xml)资源文件存放路径 mybatis.mapper-locations = classpath:mybatis/mapper/*.xml spring.aop.proxy-target-class = true4.dao层开发 首先在项目目录“/src/main/java/com/leichuangkj/async”下新建“/dao/po”目录,并在po目录下新建User实体类,具体代码如下。
@Data @NoArgsConstructor @AllArgsConstructor @ToString public class User { private Integer id; private String name; private String email; }然后在dao目录下新建mapper目录,并在mapper目录下新建UserMapper接口,具体代码如下。
@Repository public interface UserMapper { List<User> findAll(); }最后在resource目录下新建“mybatis/mapper”目录,并在mapper目录下新建UserMapper.xml文件,具体代码如下。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.leichuangkj.async.dao.mapper.UserMapper" > <resultMap id="BaseResultMap" type="com.leichuangkj.async.dao.po.User"> <constructor> <idArg column="id" javaType="java.lang.Integer" jdbcType="INTEGER" /> <arg column="name" javaType="java.lang.String" jdbcType="VARCHAR" /> <arg column="email" javaType="java.lang.String" jdbcType="VARCHAR" /> </constructor> </resultMap> <select id="findAll" resultMap="BaseResultMap"> select id, name, email from user </select> </mapper>5.service层开发 在项目目录“/src/main/java/com/leichuangkj/async”下新建service目录,并在service目录下新建IUser接口,具体代码如下。
public interface IUser { List<User> findAllSync() ; Future<List<User>> findAllAsync(); }然后在service目录下新建impl目录,并在impl目录下新建UserImpl实现类,具体代码如下。
@Service public class UserImpl implements IUser { @Autowired UserMapper userMapper; @Override public List<User> findAllSync() { try { System.out.println("开始做任务"); long start = System.currentTimeMillis(); List<User> userList = userMapper.findAll(); long end = System.currentTimeMillis(); System.out.println("完成任务,耗时:" + (end - start)+"毫秒"); return userList; }catch (Exception e){ return Collections.emptyList(); } } @Async @Override public Future<List<User>> findAllAsync() { try { System.out.println("开始做任务"); long start = System.currentTimeMillis(); List<User> userList = userMapper.findAll(); long end = System.currentTimeMillis(); System.out.println("完成任务,耗时:" + (end - start)+"毫秒"); return new AsyncResult<>(userList); }catch (Exception e){ return new AsyncResult<>(null); } } }6.controller层开发 在项目目录“/src/main/java/com/leichuangkj/async”下新建controller目录,并在controller目录下新建UserController类,具体代码如下。
@Controller @RequestMapping("/user") public class UserController { @Autowired UserImpl userImpl; @RequestMapping(value = "/findAllSync",method = RequestMethod.GET) public void findAllSync(){ long start = System.currentTimeMillis(); System.out.println("第一次查询所有用户"); List<User> userList1 = userImpl.findAllSync(); System.out.println("第二次查询所有用户"); List<User> userList2 = userImpl.findAllSync(); System.out.println("第三次查询所有用户"); List<User> userList3 = userImpl.findAllSync(); long end = System.currentTimeMillis(); System.out.println("总共消耗:"+ (end - start) + "毫秒"); } @RequestMapping(value = "/findAllAsync",method = RequestMethod.GET) public void findAllAsync() throws InterruptedException { long start = System.currentTimeMillis(); System.out.println("第一次查询所有用户"); Future<List<User>> userList1 = userImpl.findAllAsync(); System.out.println("第二次查询所有用户"); Future<List<User>> userList2 = userImpl.findAllAsync(); System.out.println("第三次查询所有用户"); Future<List<User>> userList3 = userImpl.findAllAsync(); while(true){ if(userList1.isDone()&&userList2.isDone()&&userList3.isDone()){ break; }else{ Thread.sleep(10); } } long end = System.currentTimeMillis(); System.out.println("总共消耗:"+ (end - start) + "毫秒"); } }7.启动项目 在启动类MyBatisApplication上添加注解“@MapperScan(basePackages = “com.leichuangkj.async.dao.mapper”)”和“@EnableAsync(proxyTargetClass = true)”,然后启动项目。
@MapperScan(basePackages = "com.leichuangkj.async.dao.mapper") @SpringBootApplication @EnableAsync(proxyTargetClass = true) public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } }8.测试 启动项目,然后分别在postman中请求“http://localhost:8080/user/findAllSync”和“http://localhost:8080/user/findAllAsync”,可以在控制台看到相应的执行结果,测试结果如下图所示,可以看到同步执行三次查询耗时为988毫秒,异步执行三次查询耗时772毫秒,由此可见,异步调用速度比同步调用速度快。
9.工程目录结构