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

问题

注意:这是针对常见问题的规范答案。
我有一个Spring@Serviceclass(MileageFeeCalculator)有一个@Autowiredfield(rateService),但当我尝试使用它时,该字段是null。日志显示正在创建MileageFeeCalculatorbean和MileageRateServicebean,但每当我尝试在我的服务bean上调用1595951671方法时,我得到aNullPointerException。为什么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
    }
}

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

#1 热门回答(512 赞)

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

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

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

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

##注入你的豆子

最可取的选择是让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进行注入。
通过注入@MileageFeeCalculator服务对象的标记:working-inject-bean

##使用@Configurable

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

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

Tag在服务对象上使用@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()并检索它需要的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);
    }
}

Tag通过在Spring上下文中手动查找服务对象来工作:working-manual-lookup


#2 热门回答(46 赞)

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

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

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

    }

}

#3 热门回答(21 赞)

我曾经遇到同样的问题,当时我还不习惯the life in the IoC world。我的一个bean的@Autowiredfield在运行时为null。

根本原因是,我没有使用由Spring IoC容器(其中正确注入其中的366629624字段为23488660)维护的自动创建的bean,而是使用该bean类型并使用它。当然这个person@Autowiredfield是null因为Spring没有机会注入它。