为什么我的Spring @Autowired字段为空? [英] Why is my Spring @Autowired field null?

查看:183
本文介绍了为什么我的Spring @Autowired字段为空?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我有一个Spring @Service 类( MileageFeeCalculator ),其中包含 @Autowired 字段( rateService ),但当我尝试使用它时,该字段为 null 。日志显示正在创建 MileageFeeCalculator bean和 MileageRateService bean,但我得到 NullPointerException 每当我尝试在我的服务bean上调用 mileageCharge 方法时。为什么Spring没有自动装载这个领域?



控制器类:

  @Controller 
public class MileageFeeController {
@RequestMapping(/ mileage / {miles})
@ResponseBody
public float mileageFee(@PathVariable int miles){
前程万里(MileageFeeCalculator)calc = new MileageFeeCalculator();
返回calc.mileageCharge(miles);
}
}

服务等级:

  @Service 
public class MileageFeeCalculator {

@Autowired
private MileageRateService rateService; //< ---应该是自动装配的,是null

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

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

  @服务
公共类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)
.. 。


解决方案

带注释的字段 @自动装配的 null 因为Spring不知道您创建的 MileageFeeCalculator 的副本使用 new 并且不知道自动装配它。



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



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



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



注入你的豆子



最多最好的选择是让Spring自动装配你所有的bean;这需要最少量的代码,并且是最易于维护的。要使自动装配工作符合您的要求,还要自动装载前程万里搜索,如下所示:

  @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



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

  @Service 
@Configurable
public class MileageFeeCalculator {

@Autowired
private MileageRateService rateService;

公共浮动里程数(最终英里数){
return(miles * rateService.ratePerMile());
}
}

使用<$ c工作的标记服务对象上的$ c> @Configurable : 可配置工作



手动bean查找:不推荐



此方法仅适用于在特殊情况下与遗留代码接口。创建一个Spring可以自动装配并且遗留代码可以调用的单例适配器类几乎总是优先考虑,但是可以直接向Spring应用程序上下文询问bean。



<为了做到这一点,你需要一个类,Spring可以引用 ApplicationContext 对象:

  @Component 
公共类ApplicationContextHolder实现ApplicationContextAware {
私有静态ApplicationContext上下文;

@Override
public void setApplicationContext(ApplicationContext applicationContext)抛出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);
返回calc.mileageCharge(miles);
}
}

通过手动查找Spring环境中的服务对象: working-manual-lookup


Note: This is intended to be a canonical answer for a common problem.

I have a Spring @Service class (MileageFeeCalculator) that has an @Autowired field (rateService), but the field is null when I try to use it. The logs show that both the MileageFeeCalculator bean and the MileageRateService bean are being created, but I get a NullPointerException whenever I try to call the mileageCharge method on my service bean. Why isn't Spring autowiring the field?

Controller class:

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

Service class:

@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
    }
}

Service bean that should be autowired in MileageFeeCalculator but it isn't:

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

When I try to GET /mileage/3, I get this exception:

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

解决方案

The field annotated @Autowired is null because Spring doesn't know about the copy of MileageFeeCalculator that you created with new and didn't know to autowire it.

The Spring Inversion of Control (IoC) container has three main logical components: a registry (called the ApplicationContext) of components (beans) that are available to be used by the application, a configurer system that injects objects' dependencies into them by matching up the dependencies with beans in the context, and a dependency solver that can look at a configuration of many different beans and determine how to instantiate and configure them in the necessary order.

The IoC container isn't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. When you call new, the JVM instantiates a copy of the new object and hands it straight to you--it never goes through the configuration process. There are three ways that you can get your beans configured.

I have posted all of this code, using Spring Boot to launch, at this GitHub project; you can look at a full running project for each approach to see everything you need to make it work. Tag with the NullPointerException: nonworking

Inject your beans

The most preferable option is to let Spring autowire all of your beans; this requires the least amount of code and is the most maintainable. To make the autowiring work like you wanted, also autowire the MileageFeeCalculator like this:

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

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

If you need to create a new instance of your service object for different requests, you can still use injection by using the Spring bean scopes.

Tag that works by injecting the @MileageFeeCalculator service object: working-inject-bean

Use @Configurable

If you really need objects created with new to be autowired, you can use the Spring @Configurable annotation along with AspectJ compile-time weaving to inject your objects. This approach inserts code into your object's constructor that alerts Spring that it's being created so that Spring can configure the new instance. This requires a bit of configuration in your build (such as compiling with ajc) and turning on Spring's runtime configuration handlers (@EnableSpringConfigured with the JavaConfig syntax). This approach is used by the Roo Active Record system to allow new instances of your entities to get the necessary persistence information injected.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

Tag that works by using @Configurable on the service object: working-configurable

Manual bean lookup: not recommended

This approach is suitable only for interfacing with legacy code in special situations. It is nearly always preferable to create a singleton adapter class that Spring can autowire and the legacy code can call, but it is possible to directly ask the Spring application context for a bean.

To do this, you need a class to which Spring can give a reference to the ApplicationContext object:

@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;
    }
}

Then your legacy code can call getContext() and retrieve the beans it needs:

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

Tag that works by manually looking up the service object in the Spring context: working-manual-lookup

这篇关于为什么我的Spring @Autowired字段为空?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆