Spring 6+JUnit 5中嵌套@Autowired依赖测试时无法注入
问题根因
你在ExchangeCacheImpl.loadAll()里直接new ExchangeImpl()创建的对象不在Spring容器管控范围内,Spring不会自动处理它内部的@Autowired依赖注入。生产环境正常是因为生产环境中ExchangeImpl是由Spring容器实例化的(比如通过Bean定义、工厂Bean等),而非手动new出来的。
可行解决方案
1. 让Spring接管ExchangeImpl的实例化
把ExchangeImpl声明为Spring Bean(加@Component注解,或者在配置类里用@Bean定义),然后在ExchangeCacheImpl中注入ObjectProvider<ExchangeImpl>来获取实例,替代手动new:
@Component public class ExchangeCacheImpl implements ExchangeCache { private final ObjectProvider<ExchangeImpl> exchangeProvider; // 构造注入ObjectProvider public ExchangeCacheImpl(ObjectProvider<ExchangeImpl> exchangeProvider) { this.exchangeProvider = exchangeProvider; } public void loadAll() { // 用Spring提供的方式创建实例,Spring会自动注入内部依赖 ExchangeImpl exchange = exchangeProvider.getObject(); // 后续业务逻辑... } }
同时确保ExchangeImpl上标注了@Component(或者在配置类中定义为Bean),并且它依赖的ExchangeIrregularHoursDAO、ExchangeRegularHoursDAO已经被Spring管理。
2. 测试中手动触发依赖注入
如果不想改动太多业务代码,可以在测试类里拿到ApplicationContext,手动调用Spring的AutowireCapableBeanFactory来给ExchangeImpl注入依赖:
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {YourAppConfig.class}) public class ExchangeDBTest { @Autowired private ApplicationContext ctx; @Autowired private ExchangeCache exchangeCache; @Test public void testLoadAll() { // 执行loadAll逻辑后,需要从exchangeCache里拿到创建的ExchangeImpl实例 // 这里需要ExchangeCacheImpl临时加个get方法暴露实例,测试完可以删掉或者改成包私有 ExchangeImpl exchange = ((ExchangeCacheImpl) exchangeCache).getCreatedExchange(); // 手动触发Spring的依赖注入 ctx.getAutowireCapableBeanFactory().autowireBean(exchange); // 现在调用exchange.getStandardClose(date)就不会空指针了 // 后续测试逻辑... } }
这个方案不需要修改ExchangeImpl,适合快速验证测试场景。
3. 用@Configurable+编译时织入实现自动注入
如果必须保留手动new ExchangeImpl()的写法,又想让Spring自动注入依赖,可以给ExchangeImpl加@Configurable注解,同时开启Spring的编译时织入:
@Configurable public class ExchangeImpl { @Autowired private ExchangeIrregularHoursDAO irregularHoursDAO; @Autowired private ExchangeRegularHoursDAO regularHoursDAO; // 业务方法... }
然后在Spring配置类中开启织入支持:
@Configuration @ComponentScan @EnableSpringConfigured public class AppConfig { // 其他配置... }
这种方式需要配置编译时织入(比如用AspectJ的Maven插件),对项目构建流程有要求,但不需要修改实例化代码。
踩坑提醒
- 之前尝试的原型Bean方案无效,是因为你还是用
new创建实例,没有通过Spring容器获取原型Bean。必须通过context.getBean(ExchangeImpl.class)或者ObjectProvider来获取,Spring才会处理依赖注入。 - 测试类的
@ContextConfiguration要确保包含所有必要的配置类,比如DAO层的配置、Bean扫描配置等,不然Spring找不到依赖的Bean。
内容的提问来源于stack exchange,提问作者rbrbrts

