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

chr*_*ke- 555 java null spring nullpointerexception autowired

注意:这是针对常见问题的规范答案.

我有一个Spring @Serviceclass(MileageFeeCalculator),它有一个@Autowiredfield(rateService),但该字段是null我尝试使用它时.日志显示正在创建MileageFeeCalculatorbean和MileageRateServicebean,但NullPointerException每当我尝试mileageCharge在我的服务bean上调用该方法时,我都会得到.为什么Spring没有自动装配领域?

控制器类:

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

服务类:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

应该自动装配的服务bean,MileageFeeCalculator但它不是:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试时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)
    ...
Run Code Online (Sandbox Code Playgroud)

chr*_*ke- 602

注释的字段@Autowirednull因为Spring不知道MileageFeeCalculator您创建的副本new以及不知道自动装配它的副本.

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

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

我已经在这个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要为不同的请求创建服务对象的新实例,仍可以使用Spring bean作用域进行注入.

通过注入@MileageFeeCalculator服务对象来工作的标记:working-inject-bean

使用@Configurable

如果您确实需要使用new自动装配创建的对象,则可以使用Spring @Configurable注释以及AspectJ编译时编织来注入对象.这种方法将代码插入到对象的构造函数中,该构造函数警告Spring正在创建它,以便Spring可以配置新实例.这需要在构建中进行一些配置(例如编译ajc)并打开Spring的运行时配置处理程序(@EnableSpringConfigured使用JavaConfig语法).Roo Active Record系统使用此方法允许new实体实例获取注入的必要持久性信息.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}
Run Code Online (Sandbox Code Playgroud)

通过@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 另一件需要注意的事情是在“@Configuration” bean 中为 bean 创建对象,其中创建特定 bean 类实例的方法用“@Bean”进行注释。 (2认同)
  • 您好,我遇到了类似的问题,但是当我使用您的第一个建议时,我的应用程序在调用“mileageFee”方法时认为“calc”为空。就好像它从不初始化`@Autowired MileageFeeCalculator calc`。有什么想法吗? (2认同)

Shi*_*rni 56

如果您没有编写Web应用程序的代码,请确保完成@Autowiring的类是一个spring bean.通常,弹簧容器不会意识到我们可能认为是春天的类.我们必须告诉Spring容器我们的spring类.

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

@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();

    }

}
Run Code Online (Sandbox Code Playgroud)

  • 如果你使用new创建对象,你将处理与IOC概念相矛盾的bean的生命周期.我们需要让容器去做,这样可以更好地完成它 (2认同)

小智 38

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

MileageFeeCalculator calc = new MileageFeeCalculator();
Run Code Online (Sandbox Code Playgroud)

所以它不会那样工作.

该解决方案使此MileageFeeCalculator成为Controller本身的自动连线对象.

像下面一样更改您的Controller类.

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

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

  • 这就是答案.因为你自己实例化了一个新的MilageFeeCalculator,所以Spring没有参与实例化,因此Spring spring不知道该对象存在.因此,它无法做任何事情,比如注入依赖项. (4认同)

smw*_*dia 25

我曾经不习惯的时候遇到过同样的问题the life in the IoC world.@Autowired我的一个bean 的字段在运行时为null.

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


Dee*_*pak 22

你的问题是新的(java风格的对象创建)

MileageFeeCalculator calc = new MileageFeeCalculator();
Run Code Online (Sandbox Code Playgroud)

随着注释@Service,@Component,@Configuration在创建豆
春季的应用程序上下文服务器启动时.但是当我们使用new运算符创建对象时,对象不会在已创建的应用程序上下文中注册.我使用的示例Employee.java类.

看一下这个:

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());
        }
    }
}

}
Run Code Online (Sandbox Code Playgroud)


Ash*_*ari 11

以下其中一项将起作用:

  1. 您使用 @Autowired 的类不是 Bean (我确信您可能在某个地方使用过 new() )。

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

如果以上两个不起作用......开始放置 System.out.println() 并找出哪里出了问题。

  • 你不需要做新的!您可以简单地声明类变量并在其上方使用@Autowire。您还必须确保已在要自动装配的类上方(例如,在 class ABC { ... } 上方)包含 @ Component。它会起作用的:) (2认同)

Ali*_*ahi 10

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

我们使用的@Inject不是@AutowiredSpring支持的javaee标准.每个地方都运转良好,豆子正确注入,而不是一个地方.豆注射似乎是一样的

@Inject
Calculator myCalculator
Run Code Online (Sandbox Code Playgroud)

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

所以总结一下,确保您的注释(@Autowired,@Inject,@Service,...)有正确的包!


sam*_*j90 9

@Autowired简单来说,一个字段的存在主要有两个原因:null

  • 你的班级不是春豆。

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

  • 田地不是豆子。

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


Ond*_*zek 8

另一个解决方案是 像这样调用构造函数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()); 
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这使用了不安全的发布。 (2认同)

blu*_*ish 8

我是Spring的新手,但我发现了这个有效的解决方案.请告诉我它是否是一种可折叠的方式.

applicationContext在这个bean中使用Spring :

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;       
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
Run Code Online (Sandbox Code Playgroud)

通过这种方式,任何bean都可以通过应用程序中的任何对象(也可以通过实例化new)和静态方式获得.

  • 这种模式对于使遗留代码可以访问 Spring bean 是必要的,但在新代码中应该避免。 (2认同)

nob*_*bar 6

如果这发生在测试类中,请确保您没有忘记对类进行注释。

例如,在Spring Boot 中

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
Run Code Online (Sandbox Code Playgroud)

过了一段时间...

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

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

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

@SpringBootTest
public class MyTests {
    ....
Run Code Online (Sandbox Code Playgroud)

如果您收到此错误配置你的测试将编译,但@Autowired@Value字段(例如)会null。由于 Spring Boot 是通过魔法运行的,因此您可能几乎没有办法直接调试此故障。


msu*_*cil 5

我认为您错过了指导Spring扫描带注释的类的机会。

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

@Service, @Component 等注释添加元描述。

Spring仅注入那些作为bean创建或带有注释的类的实例。

带有注解的类需要在注入之前通过spring进行识别,@ComponentScan指示spring查找带有注解的类。当Spring找到@Autowired它时,它将搜索相关的bean,并注入所需的实例。

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


Atu*_*ain 5

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


Jac*_*eur 5

一直没有什么这里提到在描述文章段落中的“执行命令”。

在“学习”我必须使用 @Component 或派生类 @Service 或 @Repository 注释一个类(我猜还有更多)之后,为了自动装配它们内部的其他组件,让我震惊的是这些其他组件在构造函数中仍然为空父组件的。

使用@PostConstruct 解决了这个问题:

@SpringBootApplication
public class Application {
    @Autowired MyComponent comp;
}
Run Code Online (Sandbox Code Playgroud)

和:

@Component
public class MyComponent {
    @Autowired ComponentDAO dao;

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

    @PostConstruct
    public void init() {
        // dao is initialized here
    }
}
Run Code Online (Sandbox Code Playgroud)