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

标签 java spring null nullpointerexception autowired

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

我有一个 Spring @Service 类 (MileageFeeCalculator),它有一个 @Autowired 字段 (rateService) ,但当我尝试使用它时,该字段为 null 。日志显示 MileageFeeCalculator bean 和 MileageRateService bean 均已创建,但每当我尝试调用 时,都会收到 NullPointerException我的服务 bean 上的 >mileageCharge 方法。为什么 Spring 不 Autowiring 该字段?

Controller 类:

@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 中 Autowiring ,但事实并非如此:

@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)
    ...

最佳答案

注释为 @Autowired 的字段为 null,因为 Spring 不知道您使用 new 创建的 MileageFeeCalculator 的副本 并且不知道如何 Autowiring 它。

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

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

我已使用 Spring Boot 发布了所有这些代码,地址为 this GitHub project ;您可以查看每种方法的完整运行项目,以了解使其发挥作用所需的一切。 带有 NullPointerException 的标记:nonworking

注入(inject)你的bean

最好的选择是让 Spring Autowiring 所有的 beans;这需要最少的代码并且最容易维护。为了使 Autowiring 按照您想要的方式工作,还可以像这样 Autowiring MileageFeeCalculator:

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

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

如果您需要为不同的请求创建服务对象的新实例,您仍然可以使用 the Spring bean scopes 来使用注入(inject).

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

使用@Configurable

如果您确实需要使用 new 创建的对象进行 Autowiring ,您可以 use the Spring @Configurable annotation along with AspectJ compile-time weaving注入(inject)你的对象。这种方法将代码插入到对象的构造函数中,提醒 Spring 它正在创建,以便 Spring 可以配置新实例。这需要在构建中进行一些配置(例如使用ajc进行编译)并打开Spring的运行时配置处理程序(使用JavaConfig语法的@EnableSpringConfigured)。 Roo Active Record 系统使用此方法来允许实体的新实例获取注入(inject)的必要持久性信息。

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

通过在服务对象上使用 @Configurable 来工作的标记:working-configurable

手动 Bean 查找:不推荐

此方法仅适用于在特殊情况下与遗留代码交互。几乎总是最好创建一个 Spring 可以 Autowiring 并且遗留代码可以调用的单例适配器类,但也可以直接向 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;
    }
}

然后您的遗留代码可以调用 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);
    }
}

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

关于java - 为什么我的 Spring @Autowired 字段为空?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56138692/

相关文章:

java - log4j:将特定类的输出记录到特定的附加程序

java - bean实例化失败;嵌套异常是 org.springframework.beans.BeanInstantiationException : Could not instantiate bean class

c++ - 取消引用空指针时是否保证段错误 (C/C++)

java - java 访问数组

java - 如何在 Eclipse 中的 Tomcat 中使用导致 InvalidJarIndexException 的损坏的 JAR 索引来追踪 JAR

java - Jar2Exe 在保护 Java 类文件不被提取时如何工作

java - 如何在 android 中运行 OpenGL 或 OpenGL ES 进行数组处理

spring - 如何使用 Spring Cloud Security 实现 OAuth2 "Token Exchange"

java - 减少 Hibernate 存储库中的样板代码/在桌面客户端-服务器应用程序中使用 Spring 的 JpaRepository 接口(interface)

sqlite - 这是 SQLite 使空值最后出现的正确语法吗?