添加@SchedulerLock后Spring定时任务集成测试报空指针异常
问题根源
添加@SchedulerLock后,Spring会为ScheduleService生成代理对象以实现锁逻辑。测试环境中,代理机制、依赖注入方式或方法访问权限的问题,可能导致代理调用目标方法时,原始对象的依赖未完成注入,从而引发空指针异常。
解决方案
1. 改用构造注入替代字段注入
字段注入可能因代理初始化顺序问题导致依赖未生效,构造注入是Spring推荐的方式,能确保对象创建时依赖已完成注入。
示例代码:
@Service public class ScheduleService { private final SomeService someService; // Spring 4.3+可省略@Autowired public ScheduleService(SomeService someService) { this.someService = someService; } @Scheduled(cron = "0 0 3 * * ?") @SchedulerLock(name = "StatisticService_processStatisticsAtThreeAM", lockAtLeastFor = "PT10M") public void methodToSchedule() { someService.method(); } }
若使用Lombok可简化为:
@RequiredArgsConstructor @Service public class ScheduleService { private final SomeService someService; @Scheduled(cron = "0 0 3 * * ?") @SchedulerLock(name = "StatisticService_processStatisticsAtThreeAM", lockAtLeastFor = "PT10M") public void methodToSchedule() { someService.method(); } }
2. 将调度方法改为public访问权限
Spring JDK动态代理仅支持public方法,即便使用CGLIB代理,非public方法的代理逻辑在测试环境中可能存在兼容性问题,改为public可避免此类问题。
3. 强制测试环境使用CGLIB代理
如果ScheduleService未实现接口,可在测试类或配置类中添加注解,强制Spring使用CGLIB代理类:
@EnableAspectJAutoProxy(proxyTargetClass = true) @ActiveProfiles("test") @ExtendWith(SpringExtension.class) @SpringBootTest @AutoConfigureMockMvc @WithMockUser @Transactional public class MyTest { // ...测试代码 }
4. 验证测试配置完整性
确保测试类通过@SpringBootTest正确加载主应用的配置(包括@EnableSchedulerLock注解),若测试使用自定义配置类,需包含ShedLock的相关配置逻辑。
5. 贴近实际场景触发调度
测试中若需调用调度方法,建议通过Spring的TaskScheduler触发,而非直接调用代理对象的方法,更贴近生产环境的执行逻辑:
@Autowired private TaskScheduler taskScheduler; @Test void testScheduledMethod() { taskScheduler.schedule(() -> service.methodToSchedule(), new Date()); // 断言逻辑 }
内容的提问来源于stack exchange,提问作者Chimitsu Nagase
相关产品推荐
相关产品推荐

