为什么我的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容器(其保持自动创建的豆@Autowired
场indeed
正确注入),我newing
我自己的这个bean类的实例,并使用它。 当然,这个@Autowired
字段为空,因为Spring没有机会注入它。