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

被折叠的 条评论
为什么被折叠?



