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

SpringBoot:在Runnable的run方法中使用@Autowired注入实例触发空指针异常的问题

问题根源分析

你触发空指针异常的核心原因非常明确:你手动通过new MyClass("abc", "def")创建的MyClass实例,根本不是Spring容器管理的Bean。

Spring的@Autowired注解只有在Spring自己创建并托管的对象实例上才会生效——它会在Bean初始化时自动注入依赖。而你手动new出来的对象,Spring完全不知道它的存在,自然不会给它注入serviceA,所以serviceA会是null,调用serviceA.innerMethod()时就会抛出NPE。

可行解决方案

下面给你几种不同场景下的解决方案,按推荐程度排序:

方案1:让MyClass由Spring托管,动态设置参数

这种方案最符合Spring的设计原则,推荐优先使用:

  1. 修改MyClass,改用构造器注入ServiceA(比字段注入更安全,支持不可变对象),同时添加参数设置方法:
@Component
public class MyClass{
    private final ServiceA serviceA;
    private String a;
    private String b;

    // 构造器注入ServiceA,Spring会自动装配
    public MyClass(ServiceA serviceA) {
        this.serviceA = serviceA;
    }

    // 链式调用的参数设置方法,方便使用
    public MyClass setParams(String a, String b) {
        this.a = a;
        this.b = b;
        return this;
    }

    public Runnable newRunnable() {
        // 用Lambda简化Runnable写法
        return () -> serviceA.innerMethod(a, b);
    }
}
  1. 在调用类中注入Spring托管的MyClass实例,再设置参数并提交任务:
// 在你的调用类中,先通过@Autowired注入MyClass
@Autowired
private MyClass myClass;

// 提交任务时动态设置参数
executor.submit(myClass.setParams("abc", "def").newRunnable());

这样myClass是Spring托管的实例,serviceA已经被正确注入,设置参数后创建的Runnable就能正常调用serviceA的方法了。

方案2:手动传递Spring托管的ServiceA实例

如果你的业务需要每次创建新的MyClass实例,可以去掉MyClass的@Component注解,把ServiceA通过构造器传入:

  1. 修改MyClass:
// 去掉@Component,让它成为普通类
public class MyClass{
    private final ServiceA serviceA;
    private String a;
    private String b;

    // 构造器同时接收ServiceA和业务参数
    public MyClass(ServiceA serviceA, String a, String b) {
        this.serviceA = serviceA;
        this.a = a;
        this.b = b;
    }

    public Runnable newRunnable() {
        return () -> serviceA.innerMethod(a, b);
    }
}
  1. 在调用类中注入ServiceA,然后手动传入MyClass的构造器:
@Autowired
private ServiceA serviceA;

// 提交任务时,把Spring托管的serviceA传进去
executor.submit(new MyClass(serviceA, "abc", "def").newRunnable());

这种方式适合需要多个独立MyClass实例的场景,同时保证serviceA是Spring注入的有效实例。

方案3:通过ApplicationContext手动获取Bean(不推荐)

如果因为某些限制必须保留原有的new MyClass(a,b)写法,可以通过实现ApplicationContextAware来手动获取Spring容器中的ServiceA,但这种方式会让代码耦合Spring容器,不利于测试和维护,仅作为临时 workaround:

@Component
public class MyClass implements ApplicationContextAware {
    private static ApplicationContext context;
    private ServiceA serviceA;
    private String a;
    private String b;

    public MyClass() {}

    public MyClass(String a, String b) {
        this.a = a;
        this.b = b;
        // 从Spring容器中手动获取ServiceA实例
        this.serviceA = context.getBean(ServiceA.class);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public Runnable newRunnable() {
        return () -> serviceA.innerMethod(a, b);
    }
}
总结

优先选择方案1或方案2,遵循Spring的依赖注入原则,避免手动创建非托管实例导致的依赖缺失问题。方案3尽量不要用,除非万不得已。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 17:47:43