You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot调用自定义restart方法重启时出现空指针异常如何解决

问题根因
  • 核心触发点是Spring Boot启动流程顺序和你预期不符:SpringApplication.run()方法在返回ConfigurableApplicationContext实例之前,就会先发布ApplicationReadyEvent事件。也就是说你标注了@EventListener(ApplicationReadyEvent.class)的saveMaths方法执行时,main方法里的context = SpringApplication.run(...)赋值操作还没执行,静态变量context此时为null,调用context.getBean自然触发空指针。
  • 潜在类名引用问题:你主启动类的类名是MainApplication,但Service类里调用的是Application.restart(),如果没有做类名别名配置,这里本身就会存在类引用错误,可以先检查下是否贴代码时类名没对齐。
  • 事务逻辑冲突:saveMaths方法加了@Transactional注解,事务提交是在方法执行完成(包括finally块执行完)之后才会触发。你在finally里直接关闭Spring上下文,会导致事务提交时数据源、事务管理器等bean已经被销毁,最终事务回滚,业务数据不会持久化到数据库。
修复方案
  1. 解决空指针核心问题:调整业务逻辑触发时机,等上下文赋值完成后再执行业务代码,修改main方法如下,同时去掉saveMaths方法上的@EventListener(ApplicationReadyEvent.class)注解,避免重复触发:
public static void main(String[] args) {
    context = SpringApplication.run(MainApplication.class, args);
    // 上下文赋值完成后再获取Service实例执行业务逻辑
    context.getBean(你自己的Service类名.class).saveMaths();
}
  1. 调整事务和重启的执行顺序:把重启逻辑移到事务提交之后再执行,不要直接写在当前事务方法的finally块里,可以用TransactionSynchronizationManager注册事务提交后的回调:
@Transactional
public void saveMaths() {
    int maths = 0;
    List<User> users;
    try {
        users = userRepository.findByMathsWhereNull();
        for (User user : users) {
            maths = calculateMaths(user);
            user.setMaths(maths);
            userRepository.updateUser(maths, user.getId());
        }
    } finally {
        // 注册事务提交后执行的回调
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    MainApplication.restart();
                }
            });
        } else {
            MainApplication.restart();
        }
    }
}
  1. 优化重启逻辑避免并发问题:给重启逻辑加锁,确保旧上下文完全关闭后再启动新上下文,修改restart方法如下:
@SpringBootApplication
public class MainApplication {

    private static ConfigurableApplicationContext context;
    private static final Object RESTART_LOCK = new Object();

    public static void main(String[] args) { 
        context = SpringApplication.run(MainApplication.class, args);
        context.getBean(你自己的Service类名.class).saveMaths();
    }

    public static void restart() {
        ApplicationArguments args = context.getBean(ApplicationArguments.class);
        synchronized (RESTART_LOCK) {
            Thread thread = new Thread(() -> {
                context.close();
                context = SpringApplication.run(MainApplication.class, args.getSourceArgs());
            });
            thread.setDaemon(false);
            thread.start();
        }
    }

}

内容的提问来源于stack exchange,提问作者Greg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 02:06:05