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

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

问题描述

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

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

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

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

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

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

当我尝试 GET/mileage/3 时,出现此异常:

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

推荐答案

注解 @Autowired 的字段是 null 因为 Spring 不知道 MileageFeeCalculator,你用 new 创建的,但不知道自动装配它.

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.

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

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.

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

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.

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

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

最可取的选择是让 Spring 自动装配所有 bean;这需要最少的代码并且最易于维护.要使自动装配如您所愿,还可以像这样自动装配 MileageFeeCalculator:

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

如果你需要为不同的请求创建一个新的服务对象实例,你仍然可以通过使用Spring bean 作用域.

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.

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

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

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

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

在服务对象上使用 @Configurable 的标签:<代码>工作可配置

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

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.

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

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

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

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

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

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

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