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

注意:这是针对常见问题的典型答案。

我有一个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
    }
}

应该在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)
    ...

注释@Autowired的字段为null因为Spring不知道您使用new创建的MileageFeeCalculator副本,但不知道自动MileageFeeCalculator它。

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

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

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

注入你的豆子

最好的选择是让Spring自动装载所有的bean; 这需要最少量的代码并且是最容易维护的。 为了使自动装配工作像你想要的那样,还MileageFeeCalculator像这样自动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标签: working-inject-bean

使用@Configurable

如果您确实需要使用new创建的对象进行自动装配,则可以使用Spring @Configurable注释以及AspectJ编译时织入来注入对象。 这种方法将代码插入到对象的构造函数中,以提醒Spring它正在创建,以便Spring可以配置新实例。 这需要在构建中进行一些配置(例如使用ajc编译)并打开Spring的运行时配置处理程序(使用JavaConfig语法配置@EnableSpringConfigured )。 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()并检索它需要的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上下文中working-manual-lookup服务对象的标签: working-manual-lookup


如果你不编码一个Web应用程序,确保你的类在@Autowiring完成是一个春天的bean。 通常情况下,春季容器不会意识到我们可能认为的春季豆类。 我们必须告诉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();

    }

}

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

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

链接地址: http://www.djcxy.com/p/62813.html

上一篇: Why is my Spring @Autowired field null?

下一篇: Spring annotations conflicts with my design guidelines