springboot测试类的子线程中注入对象报错
问题场景springboot测试类某方法如下:启动子线程,每个线程调用monitorPluginService.issueHostPlugin方法@RunWith(SpringRunner.class)@SpringBootTest(classes = Application.class)@ContextConfiguration@Slf4jclass MonitorPluginServiceIm
问题场景
springboot测试类某方法如下:
启动子线程,每个线程调用monitorPluginService.issueHostPlugin方法
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@ContextConfiguration
@Slf4j
class MonitorPluginServiceImplTest {
@Autowired
private MonitorPluginServiceImpl monitorPluginService;
@Test
void test1() {
for (int i = 0; i < 4; i++) {
new Thread(()->{
monitorPluginService.issueHostPlugin(new MonitorGroupHostModel());
}).start();
}
}
}
其中MonitorPluginServiceImpl类注入了FtpConfig
@Autowired
private FtpConfig ftpConfig;
FtpConfig类上有注解@Validated
@Validated
@Component
@Data
@ConfigurationProperties("ftp")
public class FtpConfig {
}
issueHostPlugin方法里会获取FtpConfig的属性
ftpClient = FtpUtil.openFtpClient(ftpConfig.getHost(), ftpConfig.getPort(), ftpConfig.getUser(), ftpConfig.getPassword());
结果测试时,在获取FtpConfig的属性时,抛出如下异常
org.springframework.beans.factory.BeanCreationNotAllowedException:
Error creating bean with name ‘defaultValidator’: Singleton bean
creation not allowed while singletons of this factory are in
destruction (Do not request a bean from a BeanFactory in a destroy
method implementation!)
问题跟踪
MethodValidationPostProcessor是BeanPostProcessor的一种,它的@Bean方法如下。
其中参数Validator加了@Lazy注解,于是最终生成的Validator参数是代理对象。
MethodValidationPostProcessor的作用是,如果类上加了注解@Validated,那么在实例化过程中,会为类生成代理。FtpConfig类上有注解@Validated,所以生成了FtpConfig的代理。
当进入issueHostPlugin方法时,会获取FtpConfig的属性,此时调用的是FtpConfig的代理方法。如下
进入super.proceed
红框处是MethodValidationInterceptor类,该类中的validator成员就是之前MethodValidationPostProcessor中的Validator代理
于是此时进入Validator代理方法。方法中会调用targetSource的方法
由于之前生成Validator代理时,已设置了targetSource,所以此时进入如下getTarget方法
getTarget方法开始实例化Validator。最终进入
此时从一级缓存获取不到数据,且if语句为true,于是抛出开头提到的异常。
问题分析
为什么最后能进入 if (this.singletonsCurrentlyInDestruction) {条件呢?
查看源码得知singletonsCurrentlyInDestruction初始值为false,只有当spring容器销毁时才会赋值为true。如下
测试类方法还未运行结束,为什么容器会销毁呢?
因为@Test方法逻辑是放在new Thread里的,当线程启动后,虽然业务方法monitorPluginService.issueHostPlugin还在运行,但@Test方法已结束,此时spring开始调用上述destroySingletons方法。
在业务方法里获取FtpConfig的属性时,会开始实例化Validator。由于spring已清空容器,导致无法从一级缓存获取到,而singletonsCurrentlyInDestruction又为true,于是抛出异常。
问题解决
线程启动后sleep一段时间,防止spring提前清空容器
@Test
void test2() throws InterruptedException {
for (int i = 0; i < 4; i++) {
new Thread(()->{
monitorPluginService.issueHostPlugin(new MonitorGroupHostModel());
}).start();
}
Thread.sleep(100000);
}
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)