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

注意:这是常见问题的规范答案。

我有一个Spring类()有一个字段(),但这个字段是当我尝试使用它时。日志显示正在创建 Bean 和 Bean,但是每当我尝试在我的服务 Bean 上调用该方法时,我都会得到一个。为什么Spring不自动布线?@ServiceMileageFeeCalculator@AutowiredrateServicenullMileageFeeCalculatorMileageRateServiceNullPointerExceptionmileageCharge

控制器类:

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

答案 1

注释的字段是因为Spring不知道您创建的副本,也不知道自动连接它。@AutowirednullMileageFeeCalculatornew

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

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

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

注入你的豆子

最可取的选择是让Spring自动连接所有豆子;这需要最少的代码,并且最易于维护。要使自动布线像您想要的那样工作,还可以像这样自动布线: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服务对象工作的标记:工作-注入-bean

使用@Configurable

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

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

在服务对象上使用@Configurable工作的标记:工作可配置

手动查找豆类:不推荐

此方法仅适用于在特殊情况下与旧代码进行交互。创建一个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;
    }
}

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

@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 上下文中手动查找服务对象来工作的标记:工作-手动-查找


答案 2

如果您没有编写Web应用程序,请确保您完成@Autowiring的类是一个弹簧豆。通常,弹簧容器不会知道我们可能认为是弹簧豆的类。我们必须告诉春季容器我们的春季课程。

这可以通过在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();

    }

}