@Async注解引发的报错之解决

本文探讨了在Spring Boot中使用@Lazy注解解决循环依赖的问题。通过在TaskService上添加@Lazy,避免了在初始化过程中触发循环依赖,导致的错误。详细解释了@Lazy的工作原理,当调用getTarget方法时,才会从容器中获取真实的bean,从而确保了正确注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

solution

答案

上篇文章中,我们知道了TaskServiceImpl在注入RobotServiceImpl前后从raw version变成了代理。而且我们希望找到dependentBeanMap这个map维护的地方。

在这里插入图片描述
在属性注入的代码里,我们看到了dependentBeanMap维护的地方。那么我们想:能不能不进这个方法?

其实最终的解决方案很简单,就是在注入的TaskService上加@Lazy注解:

在这里插入图片描述
TaskService就是有@Aysnc方法的服务。

问题是:为什么?

原理

假设我们加上了@Lazy注解,再次debug。容器要注入TaskController,然后属性注入的时候发现要注入TaskService,然后去创建TaskService,创建好后又要属性注入,发现有RobotService,然后又去创建RobotService,创建好后又要属性注入,我们就从这里开始。

在这里插入图片描述
在这里我们会去检查RobotServiceImpl中的TaskService属性是不是有@Lazy标签。

在这里插入图片描述
如果有这个注解,他就走buildLazyResolutionProxy(descriptor, beanName)。那我们就会进到这个方法。

在这里插入图片描述

在这里插入图片描述
他在这里生成了一个TaskService的代理返回了出去。到时候真正去获取TaskSerivce的时候会回调getTarget方法。

在这里插入图片描述
因为result有值了,所以autowiredBeanNames就是空的。

在这里插入图片描述
因为autowiredBeanNames为空,所以dependentBeanMap也没有值,或者说,他没有以TaskServiceImpl为key的值。

在这里插入图片描述
那么在RobotServiceImpl中注入的TaskService就是一个临时的代理对象。

我们走完RobotServiceImpl的生命周期,然后走TaskServiceImpl的生命周期。

在这里插入图片描述
TaskServiceImpl因为AsyncAnnotationBeanPostProcessor被做了一层代理,但是由于hasDependentBean(beanName)false,所以报错就不会出现了。

我们让TaskServiceImpl的生命周期走完。

在这里插入图片描述
在这里插入图片描述
在最后将TaskServiceImpl放到IOC容器的时候,RobotServiceImpl持有的TaskService也不是最终容器中的TaskService


那么,什么时候RobotServiceImpl才去获取真正在容器中的TaskService呢?

我们在TaskController中再增加一个要用RobotService的接口:

@RestController
public class TaskController {

    @Autowired
    private TaskService taskService;

    @Autowired
    private RobotService robotService;

    @GetMapping("/taskTime")
    public Long taskTime(){
        long start = System.currentTimeMillis();
        taskService.getTaskExecutionTime();
        long end = System.currentTimeMillis();
        return end-start;
    }

    @GetMapping("/sendCmd")
    public String sendCmd(){
        robotService.sendCmd("aaa");
        return "ok";
    }
}

我们请求sendCmd

一放行就来到了getTarget方法。

在这里插入图片描述
这时候才去容器中拿真正的TaskService

<think>嗯,用户在使用Postman测试他们用Visual Studio编写的RESTful API时遇到了错误提示:“Body was inferred but the method does not allow inferred body parameters.”。我需要先理解这个错误的原因,然后找出解决办法。 首先,这个错误通常与HTTP请求的方法和请求体的存在有关。根据HTTP协议的规定,某些方法(如GET、HEAD)通常不允许携带请求体。可能用户在使用Postman发送请求时,选择了GET方法,同时又填写了请求体数据,导致服务器端拒绝处理。 接下来,我要回忆相关的HTTP方法和请求体的规则。比如,GET请求一般用于获取数据,参数通常放在URL的查询字符串中,而不是请求体中。而POST、PUT等方法则允许携带请求体。如果后端代码在处理GET请求时,错误地配置了接收请求体参数,或者前端错误地发送了请求体,就会引发这个错误。 然后,我需要检查用户提供的引用内容,看看是否有相关的配置问题。例如,引用[1]中提到,在Spring中可以通过@RequestMapping指定请求方法,如限制某个端点只能接受POST请求。如果用户的后端代码在处理某个GET端点时,错误地配置了接收请求体,或者没有正确限制方法,可能会导致这个问题。 可能的解决步骤包括: 1. 检查Postman中的请求方法是否正确,是否误用了GET方法而携带了请求体。 2. 确认后端API的方法配置是否正确,比如是否在GET方法中错误地允许了请求体。 3. 如果是GET请求需要传递参数,改用查询参数而不是请求体。 4. 如果是POST或其他允许请求体的方法,确保后端方法正确配置了接收请求体,比如使用@RequestBody注解。 此外,用户可能需要检查他们的API代码,确保没有在GET处理方法中意外地绑定请求体参数。例如,在Spring中,如果在GET方法中使用@RequestBody注解,会导致框架尝试解析请求体,而GET请求通常不会有请求体,从而引发错误。 另一个可能性是Postman自动推断请求体参数,但实际方法不允许。例如,如果用户选择了GET方法,但Postman自动添加了请求体,这时候需要手动删除请求体内容,或者改用正确的HTTP方法。 总结下来,解决办法可能包括调整请求方法、移除不必要的请求体、使用正确的参数传递方式,以及检查后端代码的方法配置。同时,确保遵循RESTful API的最佳实践,正确使用HTTP方法。</think>### 问题分析 在Postman测试RESTful API时出现`Body was inferred but the method does not allow inferred body parameters`错误,通常是由于以下原因: 1. **HTTP方法不支持请求体**:例如`GET`、`HEAD`等方法默认不允许携带请求体,但请求中却包含`Body`参数[^1]。 2. **后端代码未正确配置**:未明确限制请求方法或未正确处理请求体绑定[^2]。 --- ### 解决方案 #### 1. 检查请求方法是否匹配 - **错误场景**:使用`GET`方法发送包含`Body`的请求。 - **修正步骤**: - 在Postman中选择支持请求体的方法(如`POST`、`PUT`)。 - 若必须用`GET`,将参数通过URL传递(如`/api?param=value`)。 #### 2. 配置后端代码的请求方法 - **示例(Spring Boot)**: ```java @PostMapping("/endpoint") // 明确限定为POST方法 public String handleRequest(@RequestBody User user) { // 处理逻辑 } ``` - 使用`@PostMapping`代替`@RequestMapping`以限制仅接受`POST`请求。 #### 3. 检查请求头格式 - 在Postman中设置`Content-Type`为`application/json`(或其他匹配后端接收的格式)。 #### 4. 验证请求体语法 - 确保`Body`内容为合法的JSON格式,避免语法错误。 --- ### 相关代码示例(Rust Actix-web) ```rust #[post("/login")] async fn login(user: web::Json<User>) -> impl Responder { // 处理逻辑 } ``` 若使用`GET`方法调用此端点,则会因方法不匹配报错。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值