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

问题:

 Note: This is intended to be a canonical answer for a common problem.
我有一个Spring @Service类(MileageFeeCalculator),它有一个@Autowired字段(rateService),但是当我尝试使用它时,该字段是null。日志显示MileageFeeCalculator bean和MileageRateService bean正在创建,但是当我尝试在我的服务bean上调用mileageCharge方法时,我收到一个NullPointerException。为什么Spring没有自动连线?
控制器类:

@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
    }
}
&#91;/code&#93;
应该在<code>MileageFeeCalculator</code>中自动连线的服务bean,但不是:

@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的副本,并且不知道自动连线。
 The Spring Inversion of Control (IoC) container有三个主要的逻辑组件:可用于应用程序的组件(bean)的注册表(称为ApplicationContext),通过将依赖关系与bean匹配来将对象的依赖关系注入到其中的配置程序系统在上下文中,以及一个可以查看许多不同bean的配置的依赖解决器,并确定如何以必要的顺序实例化和配置它们。
IoC容器不是魔术,它无法知道Java对象,除非你以某种方式通知它们。当您调用new时,JVM实例化一个新对象的副本,并将其直接传递给您 - 它不会经历配置过程。有三种方法可以让您的Bean配置好。
我已经发布了所有这些代码,使用Spring Boot启动,在this GitHub project;您可以查看一个完整的运行项目的每种方法,看看所有你需要使其工作。 标记与

注入你的豆子

最好的选择是让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);
    }
}

如果您需要为不同的请求创建一个服务对象的新实例,则仍然可以使用the Spring bean scopes
 通过注入工作的标签

使用@Configurable

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

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

 使用标签

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

那么你的遗留代码可以调用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上下文中手动查找服务对象的标签:

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Why is my Spring @Autowired field null?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

− 3 = 3