为什么我的 Spring @Autowired 字段为空?

Why is my Spring @Autowired field null?

提问人:chrylis -cautiouslyoptimistic- 提问时间:11/11/2013 最后编辑:Tarynchrylis -cautiouslyoptimistic- 更新时间:4/12/2023 访问量:727938

问:

注意:这是常见问题的规范答案。

我有一个 Spring 类 () 有一个字段 (),但该字段是当我尝试使用它时。日志显示正在创建 Bean 和 Bean,但每当我尝试在我的服务 Bean 上调用该方法时,我都会得到一个。为什么 Spring 不自动布线?@ServiceMileageFeeCalculator@AutowiredrateServicenullMileageFeeCalculatorMileageRateServiceNullPointerExceptionmileageCharge

控制器类:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = new MileageFeeCalculator();
        return calc.mileageCharge(miles);
    }
}

服务等级:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- should be autowired, is null

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); // <--- throws NPE
    }
}

应该自动连接但它不是的服务 bean:MileageFeeCalculator

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

当我尝试时,我得到这个异常:GET /mileage/3

java.lang.NullPointerException: null
    at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
    at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
    ...
java spring null nullpointerexception 自动连线

评论

6赞 aliopi 11/11/2015
另一种情况可能是在另一个 bean 的构造函数中调用 bean 。在这种情况下,将所需的 Bean 作为参数传递给其他 Bean 构造函数,并用 注释 的构造函数。记得用 注释第一个 Bean 的类。FSFSS@AutowireF@Component
2赞 Ross117 11/14/2018
我在这里使用 Gradle 编写了几个与此非常相似的示例:github.com/swimorsink/spring-aspectj-examples。希望有人会发现它有用。

答:

782赞 chrylis -cautiouslyoptimistic- 11/11/2013 #1

注释的字段是因为 Spring 不知道您创建的副本,也不知道自动连接它。@AutowirednullMileageFeeCalculatornew

Spring 控制反转 (IoC) 容器有三个主要的逻辑组件:可供应用程序使用的组件 (bean) 的注册表(称为)、配置器系统,该系统通过将依赖关系与上下文中的 bean 匹配来将对象的依赖关系注入其中,以及依赖关系求解器,它可以查看许多不同 bean 的配置并确定如何以必要的顺序实例化和配置它们。ApplicationContext

IoC 容器并不神奇,除非你以某种方式通知它,否则它无法了解 Java 对象。当您调用 时,JVM 会实例化新对象的副本并将其直接交给您 - 它永远不会经过配置过程。有三种方法可以配置 bean。new

我已经在这个 GitHub 项目中发布了所有这些代码,使用 Spring Boot 启动;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切。带有 NullPointerException 的标记:nonworking

注入你的豆子

最可取的选择是让 Spring 自动连接所有 bean;这需要最少的代码,并且是最容易维护的。要使自动接线按您想要的方式工作,还可以像这样自动接线:MileageFeeCalculator

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

如果需要为不同的请求创建服务对象的新实例,您仍然可以使用 Spring Bean 作用域使用注入。

通过注入 @MileageFeeCalculator 服务对象起作用的标签:working-inject-bean

使用@Configurable

如果你真的需要自动连接创建的对象,你可以使用 Spring @Configurable 注解和 AspectJ 编译时编织来注入你的对象。这种方法将代码插入到对象的构造函数中,以提醒 Spring 正在创建它,以便 Spring 可以配置新实例。这需要在构建中进行一些配置(例如使用 编译)并打开 Spring 的运行时配置处理程序(使用 JavaConfig 语法)。Roo Active Record 系统使用此方法,以允许实体的实例获取注入的必要持久性信息。newajc@EnableSpringConfigurednew

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

在服务对象上使用@Configurable的标记:working-configurable

手动 Bean 查找:不推荐

此方法仅适用于在特殊情况下与遗留代码进行交互。创建一个 Spring 可以自动连接并且遗留代码可以调用的单例适配器类几乎总是更可取的,但可以直接向 Spring 应用程序上下文请求 bean。

为此,您需要一个 Spring 可以引用对象的类:ApplicationContext

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

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

    public static ApplicationContext getContext() {
        return context;
    }
}

然后,您的遗留代码可以调用和检索它需要的 bean:getContext()

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
        return calc.mileageCharge(miles);
    }
}

通过在 Spring 上下文中手动查找服务对象来工作的标签:working-manual-lookup

评论

2赞 Donal Fellows 11/11/2013
要查看的另一件事是在 Bean 中为 Bean 创建对象,其中创建特定 Bean 类实例的方法用 注释。@Configuration@Bean
1赞 chrylis -cautiouslyoptimistic- 11/11/2013
@DonalFellows 我不完全确定你在说什么(“制造”是模棱两可的)。您是在谈论使用 Spring Proxy AOP 时多次调用方法的问题吗?@Bean
2赞 Theo 7/15/2014
您好,我遇到了类似的问题,但是当我使用您的第一个建议时,我的应用程序在调用“mileageFee”方法时认为“calc”为空。就好像它从不初始化 .有什么想法吗?@Autowired MileageFeeCalculator calc
0赞 Sotirios Delimanolis 10/18/2014
我认为您应该在答案的顶部添加一个条目,说明检索第一个 bean(您从中执行所有操作的根)应该通过 .一些用户(我已经将其作为重复项关闭)不明白这一点。ApplicationContext
1赞 Priidu Neemre 9/3/2015
如果我错了,请纠正我,但根据 Spring AOP 文档,在 和 上指定注释可能不正确:......确保不要在容器中注册为常规 Spring Bean 的 Bean 类上使用 @Configurable: 否则,您将获得双重初始化,一次通过容器,一次通过方面。所以从本质上讲,你应该只选择其中之一。@Service@ConfigurableMilegageFeeCalculator
72赞 Shirish Coolkarni 4/22/2014 #2

如果您不是编写 Web 应用程序,请确保完成@Autowiring的类是 spring bean。通常,spring container 不会意识到我们可能认为是 spring bean 的类。我们必须告诉 Spring 容器我们的春季课程。

这可以通过在 appln-contxt 中配置来实现,或者更好的方法是将类注释为 @Component,请不要使用 new 运算符创建注释类。 确保从 Appln-context 获取它,如下所示。

@Component
public class MyDemo {


    @Autowired
    private MyService  myService; 

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            System.out.println("test");
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("ctx>>"+ctx);

            Customer c1=null;
            MyDemo myDemo=ctx.getBean(MyDemo.class);
            System.out.println(myDemo);
            myDemo.callService(ctx);


    }

    public void callService(ApplicationContext ctx) {
        // TODO Auto-generated method stub
        System.out.println("---callService---");
        System.out.println(myService);
        myService.callMydao();

    }

}

评论

0赞 Ashish 12/29/2014
嗨,我浏览了您的解决方案,没错.在这里,我想知道“为什么我们不使用新运算符创建带注释的类的实例,我可以知道这背后的原因吗?
4赞 Shirish Coolkarni 8/20/2015
如果使用 new 创建对象,则将处理 Bean 的生命周期,这与 IOC 的概念相矛盾。我们需要要求容器来做这件事,这会以更好的方式做到这一点
7赞 Ondrej Bozek 4/21/2015 #3

另一种解决方案是像这样调用
构造函数:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)MileageFeeCalculator

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}

评论

1赞 chrylis -cautiouslyoptimistic- 4/21/2015
这使用不安全的发布。
15赞 bluish 5/14/2015 #4

我是 Spring 的新手,但我发现了这个可行的解决方案。请告诉我这是否是一种可弃用的方式。

我让 Spring 注入这个 bean:applicationContext

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils {

    public static ApplicationContext ctx;

    /**
     * Make Spring inject the application context
     * and save it on a static variable,
     * so that it can be accessed from any point in the application. 
     */
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;       
    }
}

如果需要,也可以将此代码放在主应用程序类中。

其他类可以像这样使用它:

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);

这样,任何 Bean 都可以由应用程序中的任何对象(也用 指定)以静态方式获得。new

评论

2赞 chrylis -cautiouslyoptimistic- 5/14/2015
这种模式对于使 Spring Bean 可供遗留代码访问是必要的,但在新代码中应避免使用。
0赞 Joginder Malik 5/30/2020
就我而言,我需要这个,因为第三方类很少。Spring(IOC)无法控制它们。这些类从未从我的 Spring Boot 应用程序中调用过。我遵循了这种方法,它对我有用。
26赞 Deepak 10/8/2015 #5

您的问题是新的(以 java 样式创建对象)

MileageFeeCalculator calc = new MileageFeeCalculator();

使用注解 , ,当服务器启动时,将在 Spring
的应用程序上下文中创建 bean。但是当我们创建对象时 使用 new 运算符时,对象不会在已创建的应用程序上下文中注册。例如,我使用的 Employee.java 类。
@Service@Component@Configuration

看看这个:

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        String name = "tenant";
        System.out.println("Bean factory post processor is initialized"); 
        beanFactory.registerScope("employee", new Employee());

        Assert.state(beanFactory instanceof BeanDefinitionRegistry,
                "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

        for (String beanName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
            if (name.equals(definition.getScope())) {
                BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
                registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
            }
        }
    }
}
35赞 smwikipedia 11/12/2015 #6

当我不太习惯 IoC 世界的生活时,我曾经遇到过同样的问题。我的一个 Bean 的字段在运行时为 null。@Autowired

根本原因是,我没有使用由 Spring IoC 容器维护的自动创建的 bean(其字段确实被正确注入),而是正在构建我自己的该 bean 类型的实例并使用它。当然,这个字段是空的,因为 Spring 没有机会注入它。@Autowirednew@Autowired

评论

1赞 Abdulbasith 2/21/2022
这个永远帮助了我!
1赞 PowerAktar 7/21/2022
只要对象本身不依赖于 IoC,就可以这样做new
15赞 Alireza Fattahi 6/11/2016 #7

这似乎很少见,但这是发生在我身上的事情:

我们用的是 Spring 支持的 javaee 标准。每个地方都工作正常,豆子注射正确,而不是一个地方。豆子注射似乎是一样的@Inject@Autowired

@Inject
Calculator myCalculator

最后我们发现错误是我们(实际上是 Eclipse 自动完成功能)导入而不是 !com.opensymphony.xwork2.Injectjavax.inject.Inject

所以总而言之,确保你的注解(、、,...)有正确的包!@Autowired@Inject@Service

54赞 Ravi Durairaj 7/26/2016 #8

实际上,您应该使用 JVM 管理的对象或 Spring 管理的对象来调用方法。 从控制器类中的上述代码中,您将创建一个新对象来调用具有自动连接对象的服务类。

MileageFeeCalculator calc = new MileageFeeCalculator();

所以它不会那样工作。

该解决方案使此 MileageFeeCalculator 成为控制器本身中的自动连线对象。

更改 Controller 类,如下所示。

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

评论

4赞 cyotee doge 9/20/2017
这就是答案。因为你是自己实例化一个新的MilageFeeCalculator,所以Spring不参与实例化,所以Spring spring不知道该对象的存在。因此,它不能对它做任何事情,比如注入依赖项。
2赞 Abhishek 10/14/2016 #9

您还可以使用对服务类进行@Service注释,并将所需的 Bean classA 作为参数传递给其他 Bean classB 构造函数,并使用 @Autowired 注释 classB 的构造函数来解决此问题。示例片段在这里:

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}

评论

0赞 CruelEngine 3/5/2018
这对我有用,但你能详细说明一下这是如何解决问题的吗?
1赞 Abhishek 4/11/2018
@CruelEngine,看这是构造函数注入(你显式设置一个对象),而不仅仅是使用字段注入(这主要是通过弹簧配置完成的)。因此,如果您使用“new”运算符创建 ClassB 的对象是其他一些范围,那么该范围将不可见或为 ClassA 自动连接设置。因此,在调用 classB.useClassAObjectHere() 时,如果您只声明字段 Injection,则会抛出 NPE,因为 classA 对象不会自动连接。阅读 chrylis 试图解释同样的事情。这就是为什么建议使用构造函数注入而不是场注入的原因。现在有意义吗?
6赞 msucil 1/11/2017 #10

我认为您错过了指示 spring 使用注释扫描类。

您可以在 Spring 应用程序的配置类上使用 Spring 来指示 spring 进行扫描。@ComponentScan("packageToScan")

@Service, @Componentetc 注解添加元描述。

Spring 只注入那些被创建为 bean 或用注解标记的类的实例。

标有注解的类在注入前需要用spring标识,指示spring寻找标有注解的类。当 Spring 找到它时,它会搜索相关的 bean,并注入所需的实例。@ComponentScan@Autowired

仅添加注解,并不能修复或促进依赖注入,Spring 需要知道在哪里寻找。

评论

0赞 Ralph Callaway 12/5/2018
当我忘记添加到我的文件时遇到了这个问题<context:component-scan base-package="com.mypackage"/>beans.xml
4赞 62mkv 10/12/2017 #11

更新:真正聪明的人很快就指出了这个答案,这解释了下面描述的奇怪之处

原答案:

我不知道它是否对任何人有帮助,但即使做看似正确的事情,我也遇到了同样的问题。在我的 Main 方法中,我有这样的代码:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

在一个文件中,我有一行token.xml

<context:component-scan base-package="package.path"/>

我注意到package.path不再存在,所以我刚刚永远删除了这一行。

在那之后,NPE开始进来。在一个 我只有 2 颗豆子:pep-config.xml

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

和 SomeAbac 类有一个声明为

@Autowired private Settings settings;

由于某种未知原因,当元素根本不存在时,init() 中的设置为 null,但是当它存在并且有一些 bs 作为 basePackage 时,一切正常。此行现在如下所示:<context:component-scan/>

<context:component-scan base-package="some.shit"/>

它起作用了。也许有人可以提供解释,但对我来说现在已经足够了:)

评论

5赞 ForNeVeR 10/12/2017
这个答案就是解释。 隐式启用工作所需的条件。<context:component-scan/><context:annotation-config/>@Autowired
9赞 Brent Bradburn 4/13/2018 #12

如果这种情况发生在测试类中,请确保您没有忘记为类添加注释。

例如,在 Spring Boot 中:

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....

一段时间过去了......

Spring Boot 不断发展如果您使用正确版本的 JUnit,则不再需要使用它。@RunWith

要独立工作,您需要使用 JUnit5 而不是 JUnit4@SpringBootTest@Test

//import org.junit.Test; // JUnit4
import org.junit.jupiter.api.Test; // JUnit5

@SpringBootTest
public class MyTests {
    ....

如果此配置错误,则测试将编译,但字段(例如)将为 .由于 Spring Boot 的运行方式很神奇,因此您可能几乎没有直接调试此故障的途径。@Autowired@Valuenull

评论

0赞 Brent Bradburn 4/9/2020
Смотритетакже: stackoverflow.com/questions/4130486/...
0赞 Brent Bradburn 4/9/2020
注意:与字段一起使用时将为 null。@Valuestatic
0赞 Brent Bradburn 6/2/2020
Spring 提供了许多失败的方法(没有编译器的帮助)。当出现问题时,你最好的选择是回到原点——只使用你知道可以协同工作的注释组合。
4赞 yglodt 1/11/2019 #13

另请注意,如果出于某种原因,您在 as 中创建了一个方法,您将从中访问的自动连线 bean 将始终是 。@Servicefinalnull

评论

1赞 aakoch 8/19/2022
我花了一天的时间在这个问题上敲打我的头。谢谢。你是神。:D
5赞 Atul Jain 5/6/2019 #14

这是导致 NullPointerException 的罪魁祸首 我们正在使用 Spring - 不需要手动创建对象。对象创建将由 IoC 容器处理。MileageFeeCalculator calc = new MileageFeeCalculator();

14赞 JackLeEmmerdeur 7/5/2019 #15

本文在“执行顺序”段落中描述了此处未提及的内容。

在“学习”我必须用@Component或导数@Service或@Repository(我想还有更多)注释一个类之后,自动连接它们内部的其他组件,令我震惊的是,这些其他组件在父组件的构造函数中仍然是空的。

使用 @PostConstruct 可以解决以下问题:

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}

和:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

    public MyComponent() {
        // dao is null here
    }

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
9赞 SAMUEL 11/27/2019 #16

简单来说,一个字段主要有两个原因@Autowirednull

  • 你的班级不是春豆。

定义注释的类不是 spring bean。因此,弹簧不会自动连接杆件。@Autowire

  • 田地不是豆子。

在 Spring 应用程序上下文或注册表中尚不存在您在字段中指定的类型或层次结构中的类型的 bean@Autowired

1赞 Rishabh Agarwal 12/21/2019 #17

这仅在单元测试的情况下有效。

我的 Service 类有一个 service 的注释,它是另一个组件类。当我测试时,组件类为 null。因为对于服务类,我使用@autowirednew

如果您正在编写单元测试,请确保您没有使用 创建对象。请改用 injectMock。new object()

这解决了我的问题。这是一个有用的链接

0赞 Arefe 5/11/2020 #18

与问题不完全相关,但如果字段注入为 null,则基于构造函数的注入仍然可以正常工作。

    private OrderingClient orderingClient;
    private Sales2Client sales2Client;
    private Settings2Client settings2Client;

    @Autowired
    public BrinkWebTool(OrderingClient orderingClient, Sales2Client sales2Client, Settings2Client settings2Client) {
        this.orderingClient = orderingClient;
        this.sales2Client = sales2Client;
        this.settings2Client = settings2Client;
    }
11赞 Ashutosh Tiwari 10/25/2020 #19

以下方法之一将起作用:

  1. 您使用 @Autowired 的类不是 Bean(我敢肯定您可能在某处使用了 new())。

  2. 在SpringConfig类中,您没有提到Spring应该@Component查找的软件包(我说的是@ComponentScan(basePackages“here”))

如果以上两个不起作用....开始放置 System.out.println() 并找出它出错的地方。

评论

1赞 Soumik Das 3/17/2021
我在代码中使用了new(),这导致了问题。我需要使用new()。但也需要在那个新类中使用@Autowire。怎么做。
2赞 Ashutosh Tiwari 3/20/2021
你不需要做新的!您可以简单地声明类变量并在其上方使用 @ Autowire。您还必须确保在要自动连接的类上方包含 @ Component(例如,在类 ABC { ... } 之上)。它将:)工作
-6赞 Qin Kai 10/26/2020 #20

如果您使用的是某个方法,它将是 ,尝试更改为 In Controller。privatenullprivatepublic

评论

0赞 ennth 7/6/2021
我不认为访问修饰符在这里有任何关系
0赞 Sean 12/23/2020 #21

另外,不要注入到成员身上,它会是.staticnull