为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

我有一个Spring @Serviceclass(MileageFeeCalculator),它有一个@Autowiredfield(rateService),但该字段是null我尝试使用它时。日志显示正在创建MileageFeeCalculatorbean和MileageRateServicebean,但NullPointerException每当我尝试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

    }

}

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


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

    ...

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

查看完整描述

6 回答

?
波斯汪

注释的字段@Autowirednull因为Spring不知道MileageFeeCalculator您创建的副本new以及不知道自动装配它的副本。

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

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

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

注入你的豆子

最可取的选择是让Spring自动装配所有bean; 这需要最少量的代码,并且是最易维护的。要使自动装配工作与您想要的MileageFeeCalculator一样,也可以通过以下方式自动装配:

@Controllerpublic 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@Configurablepublic 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对象提供引用的类:

@Componentpublic 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:

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


查看完整回答
反对 回复 2019-05-21
?
qq_笑_17

如果您没有编写Web应用程序的代码,请确保完成@Autowiring的类是一个spring bean。通常,弹簧容器不会意识到我们可能认为是春天的类。我们必须告诉Spring容器我们的spring类。

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

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

    }}


查看完整回答
反对 回复 2019-05-21
?
慕姐829404

实际上,您应该使用JVM托管对象或Spring托管对象来调用方法。根据控制器类中的上述代码,您将创建一个新对象来调用具有自动连接对象的服务类。

MileageFeeCalculator calc = new MileageFeeCalculator();

所以它不会那样工作。

该解决方案使此MileageFeeCalculator成为Controller本身的自动连线对象。

像下面一样更改您的Controller类。

@Controllerpublic class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

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


查看完整回答
反对 回复 2019-05-21
?
慕标5265247

我曾经不习惯的时候遇到过同样的问题the life in the IoC world@Autowired我的一个bean 的字段在运行时为null。

根本原因是,我不是使用由Spring IoC容器(其@Autowired字段被indeed正确注入)维护的自动创建的bean,而是newing我自己的bean类型的实例并使用它。当然这个@Autowired字段是空的,因为Spring没有机会注入它。


查看完整回答
反对 回复 2019-05-21
?
智慧大石

你的问题是新的(java风格的对象创建)

MileageFeeCalculator calc = new MileageFeeCalculator();

随着注释@Service@Component@Configuration在创建豆
春季的应用程序上下文服务器启动时。但是当我们使用new运算符创建对象时,对象不会在已创建的应用程序上下文中注册。我使用的示例Employee.java类。

看一下这个:

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory
(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String name = "tenant";
    System.out.println("Bean factory post processor is initialized"); 
    beanFactory.registerScope("employee", new Employee());

    Assert.state(beanFactory instanceof BeanDefinitionRegistry,
            "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (name.equals(definition.getScope())) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName),
            registry, true);
            registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
        }
    }}}


查看完整回答
反对 回复 2019-05-21
?
烧仙草VB

这似乎是罕见的情况,但这是发生在我身上的事情:

我们使用的@Inject不是@AutowiredSpring支持的javaee标准。每个地方都运转良好,豆子正确注入,而不是一个地方。豆注射似乎是一样的

@InjectCalculator myCalculator

最后我们发现错误是我们(实际上,Eclipse自动完成功能)导入com.opensymphony.xwork2.Inject而不是javax.inject.Inject

所以总结一下,确保您的注释(@Autowired@Inject@Service,...)有正确的包!


查看完整回答
反对 回复 2019-05-21

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信