@TOC
问题背景
模型搜索算法侧召回出现了badCase,需要对其进行问题排查,以往的人工排查流程划分了很多步骤,现在服务端需要把每一个step的返回值情况串联起来,获得最终的排查结果,流程图结构如下。
一、基本功能实现
经过上一篇文章引入状态模式的3步修改,代码从原来的80行优化成了7个状态类+1个状态机+1个不超过10行的核心方法的结构,结构变得更加清晰,实现了状态封装和逻辑流程解耦,很大程度上优化了代码,赏心悦目。
重新测试代码,发现能够完全实现对应流程图的判定需求,但是中途出现了亿点点难以理解的小问题,所以导致我不得不对这个小问题继续进行深入探究。
小剧场:中途出现的bean扫描与传递问题
(1)bean加载扫描为null
在做这整个流程的单元测试的时候出现了一个难以理解的groundingService一直为null的问题。首先我这个单元测试是在test包下面的,如果通过生成实例GroundingService groundingService = new GroundingServiceImpl(); 这种方式调badCaseDetect(userInputParam);方法时,会报错groundingService为null ; 然而我通过 @Resource GroundingService groundingService; 注入了这个以后调用这个注入的groundingService就不会报错了
java
@Resource
GroundingService groundingService;
@Test
public void testAllProgressRes() {
GroundingDTO userInputParam = new GroundingDTO();
userInputParam.setXXX("XXXXXX");
userInputParam.setXXX("XXXXXX");
userInputParam.setXXX("XXXXXX");
// GroundingService groundingService = new GroundingServiceImpl();
Object finalRes = groundingService.badCaseDetect(userInputParam);
System.out.println(finalRes);
}
对此,GPT给出了答案:
总结起来意思是意思是: 使用 @Resource 注解时,Spring 框架会负责实例化 GroundingService 并将其依赖的其他组件也一并初始化,但是直接实例化 GroundingServiceImpl 这种方式并不能保证 GroundingService 依赖的其他组件也被正确地初始化,如果这些组件没有被正确地初始化,那么 groundingService 就可能为 null ,所以在 Spring 中,我们通常不直接实例化服务类,而是通过 Spring 的依赖注入功能来获取服务类的实例依赖注入.
并且还需要注意,这个测试类应该包括一些基本的属性,比如组件扫描的配置,可以通过注解方式进行继承,不然也会找不到service
如示例中需要先继承BaseTest属性,就包括了@ComponentScan(basePackages = "XXXX")的配置
(2)状态转移过程上下文bean传递问题
由于状态机里面的execute方法以及每个State类里面的execute方法内部都需要调用 @Service标注的GroundingService groundingService涉及到的对应内部方法。
java
abstract public FinalResult execute(GroundingService groundingService, GroundingDTO groundingDTO, FinalResult finalResult);
java
public FinalResult execute(GroundingService groundingService, GroundingDTO groundingDTO) {
(1)最初写法:用的时候再注入,所有透传的groundingService都去掉,直接在7个State里面注入@Resource GroundingService groundingService; 修改之后发现运行起来报错,出现groundingService为null的问题。 猜测可能和bean的生命周期有关,state子类可能拿到了一个没有经过初始化的bean,所以报错值为null
(2)修正的写法:手动管理和透传groundingService(不推荐) 由于害怕运行时候还是会报出groundingService为null的问题,于是想到可以直接透传test层面的groundingServie,用this进行替代,如: finalResult = stateMachine.execute(this,groundingDTO); // 执行状态机并获取结果。这种方法虽然能解决问题,然而此方案可能并不是最佳实践,因为它将测试代码和业务逻辑紧密地耦合在一起。理想情况下,你应该能够让Spring框架负责管理所有的依赖关系,而不是在测试代码中手动管理。
java
//test层:
@Resource
GroundingService groundingService;
// ........
Object finalRes = groundingService.badCaseDetect(userInputParam);
-----------------------------------------------------------
//GroundingServiceImpl层:
@Resource
GroundingService groundingService;
finalResult = stateMachine.execute(groundingService,groundingDTO);
// 下面这样改也不会出现null的问题
// finalResult = stateMachine.execute(this,groundingDTO); // 执行状态机并获取结果
(3)更进一步写法:test层和serviceImpl都引入了@Resource GroundingService groundingService; 之后有试过在每个出现groundingService的地方都注入 @Resource GroundingService groundingService; 运行起来也没有问题
所以推测@Resource GroundingService groundingService;这种方式无论在哪一层,加载的是同一个@Service的bean,至于为什么每次我们注入的都是@Resource GroundingService groundingService,而不直接注入GroundingServiceImpl
提问: 为什么每次我们注入的都是@Resource GroundingService groundingService,而不直接注入GroundingServiceImpl 如果有多个类实现了这个接口 我怎么知道我要调用哪个 GPT给的答案:
总结
案例问题涉及到Spring框架容器对于bean的加载与管理,对于研发过程中出现的匪夷所思的小问题,推荐还是认真研究明白深层此原因,不要觉得侥幸改成功了就万事大吉,有可能这个小问题的底层是一个以前没有注意过的盲点,若不处理就很有可能会给后面埋下大坑(玄冥黑洞)。