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

新手上路,请多包涵

注意:这旨在成为常见问题的规范答案。

I have a Spring @Service class ( MileageFeeCalculator ) that has an @Autowired field ( rateService ), but the field is null 当我尝试使用它时。 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 我的服务 bean 上的方法。为什么 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
    }
}

应该在 MileageFeeCalculator 中自动装配的服务 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)
    ...

原文由 chrylis -cautiouslyoptimistic- 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 833
2 个回答

The field annotated @Autowired is null because Spring doesn’t know about the copy of MileageFeeCalculator that you created with new and didn’t知道自动装配它。

Spring 控制反转 (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);
    }
}

如果您需要为不同的请求创建服务对象的新实例,您仍然可以通过使用 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());
    }
}

通过在服务对象上使用 @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;
    }
}

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

 @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

原文由 chrylis -cautiouslyoptimistic- 发布,翻译遵循 CC BY-SA 3.0 许可协议

如果您不编写 Web 应用程序,请确保完成 @Autowiring 的类是一个 spring bean。通常,spring 容器不会知道我们可能认为是 spring bean 的类。我们必须将我们的 spring 类告诉 Spring 容器。

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

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

    }

}

原文由 Shirish Coolkarni 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题