欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > @Async使用

@Async使用

2025/11/19 4:57:29 来源:https://blog.csdn.net/qq_42108331/article/details/145310526  浏览:    关键词:@Async使用

说明:Spring Boot自带的 @Async 注解,加在方法上,可以异步执行。当你需要做异步操作时,与其引入庞大的MQ组件,不妨尝试用这个注解。本文介绍如何使用 @Async 注解,及使用上的一些注意事项。

搭建Demo

首先,搭建一个Demo,pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><groupId>com.hezy</groupId><artifactId>async_demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency></dependencies></project>

实体类,User

import lombok.Data;import java.io.Serializable;@Data
public class User implements Serializable {private String id;private String username;private String password;private String test;
}

DTO,用于接收参数和返回数据

import lombok.Data;import java.io.Serializable;@Data
public class UserDTO implements Serializable {private String id;private String username;private String password;
}

AsyncController,用于演示

import com.hezy.pojo.UserDTO;
import com.hezy.service.UserServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("async")
@Log4j2
public class AsyncController {@Autowiredprivate UserServiceImpl userService;@PostMapping("test1")public List<UserDTO> testAsync1(@RequestBody UserDTO userDTO) {log.info("进入接口");return userService.insert1(userDTO);}
}

UserServiceImpl,先插入,插入后查询

import com.hezy.mapper.UserMapper;
import com.hezy.pojo.UserDTO;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
@Log4j2
public class UserServiceImpl {@Autowiredprivate AsyncServiceImpl asyncService;@Autowiredprivate UserMapper userMapper;@Transactional(rollbackFor = Exception.class)public List<UserDTO> insert1(UserDTO userDTO) {log.info("进入service");asyncService.insertDTO1(userDTO);return userMapper.selectAll();}
}

AsyncServiceImpl,异步操作实现类

import com.hezy.mapper.UserMapper;
import com.hezy.pojo.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class AsyncServiceImpl {@Autowiredprivate UserMapper userMapper;@Asyncpublic void insertDTO1(UserDTO userDTO) {userMapper.insertUser(userDTO);}
}

UserMapper,数据库操作

import com.hezy.pojo.UserDTO;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface UserMapper {@Insert("insert into i_users (id, username, password) values(#{user.id}, #{user.username}, #{user.password})")void insertUser(@Param("user") UserDTO user);@Select("select * from i_users")List<UserDTO> selectAll();
}

启动类,需要加 @EnableAsync 注解

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;@EnableAsync
@SpringBootApplication
@MapperScan("com.hezy.mapper")
public class Start {public static void main(String[] args) {SpringApplication.run(Start.class, args);}
}

测试

为了效果明显,AsyncServiceImpl 里面的insertDTO1方法,使线程休眠5秒钟,如下:

    @Asyncpublic void insertDTO1(UserDTO userDTO) {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}userMapper.insertUser(userDTO);}

接下来,调用接口,新增一条记录。新增前,数据库如下:

在这里插入图片描述

可以看到调用接口后,马上返回结果了。

在这里插入图片描述

数据库添加成功

在这里插入图片描述

这里隐含一个问题:需要考虑到异步操作的后续查询,可能会因为异步操作未完成而未能查询到预期的数据。把线程休眠代码去掉,还是未能查询出此次新增的记录,但不使用 @Async 注解,同步操作,插入后的查询是能查出完整的数据库记录的,如下:

(同步操作)

    // @Asyncpublic void insertDTO1(UserDTO userDTO) {userMapper.insertUser(userDTO);}

(能查出完整的数据库记录)

在这里插入图片描述

另外,需要注意:

  • @Async 只能在被Spring管理的Bean内生效,即类被打上@Componet或@Service注解

  • 不能在本类中生效,被调用的异步方法不能是本类的成员方法;

更进一步

接着,再来讨论使用 @Async 异步操作的事务问题,如下,在异步操作里手动制造一个异常

(AsyncController,新增一个接口)

    @PostMapping("test2")public List<UserDTO> testAsync2(@RequestBody UserDTO userDTO) {log.info("进入接口");return userService.insert2(userDTO);}

(UserServiceImpl)

    @Transactional(rollbackFor = Exception.class)public List<UserDTO> insert2(UserDTO userDTO) {log.info("进入service");asyncService.insertDTO2(userDTO);return userMapper.selectAll();}

(在异步操作里手动制造一个异常)

    @Asyncpublic void insertDTO2(UserDTO userDTO) {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}userMapper.insertUser(userDTO);// 手动制造一个异常,测试事务int i = 1 / 0;}

调用接口,触发异常,接口没有返回异常(思考:或许这是个启发,如果你有段逻辑,调用了某个方法,为了避免此方法内抛出了异常而阻塞主逻辑,就可以将此方法改为异步操作。当然,也需要业务上允许,如果主逻辑需要此方法的返回结果,就不能考虑异步)

在这里插入图片描述

延迟5秒后,控制台抛出了异常

在这里插入图片描述

事务没有控制住,数据插入成功。不加 @Async 注解,这种情况事务是能控制住的。

在这里插入图片描述

所以,要在异步操作内控制事务,需要在异步方法上再加上一个事务注解,如下:

    @Transactional(rollbackFor = Exception.class)@Asyncpublic void insertDTO2(UserDTO userDTO) {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}userMapper.insertUser(userDTO);// 手动制造一个异常,测试事务int i = 1 / 0;}

重试,添加一条记录

在这里插入图片描述

休眠5秒后,代码执行,控制台抛出异常

在这里插入图片描述

刷新数据库,没有新增成功,事务生效了。

在这里插入图片描述

总结

本文介绍了在Spring Boot中 @Async 的使用,使用 @Async 时,总结以下几点:

  • 启动类需要加 @EnableAsync 注解;

  • 只能在被Spring管理的Bean内生效,即类被打上 @Componet 或 @Service 注解

  • 不能在本类中生效,调用的异步方法不能是本类的成员方法;

  • 异步操作的后续查询,可能会因为异步操作未完成而未能查询到预期的数据;

  • 调用异步操作方法上的声明式事务无法控制异步操作内的事务,如需把控异步操作内的事务,需另外在异步操作方法上加事务注解;

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词