stackoverflow.com/questions/1…
背景
@Controller
public class MileeageFeeController {
@RequestMapping("mileage/{miles}")
@ResponseBody
public float mileeageFee(@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
}
}
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
上面的代码会报出空指针。
高赞回答
注解@Autowired的字段是空的,因为Spring不知道你用new创建的MileageFeeCalculator的副本,也不知道要自动连接它。
Spring IOC 容器有三个主要的逻辑组件:一个可供应用程序使用的组件(Bean)的注册表(ApplicationContext),一个通过将依赖关系与上下文中的 Bean 相匹配而将对象的依赖关系注入其中的 configurer 系统,以及一个可以查看许多不同 Bean 的配置并确定如何以必要的顺序实例化和配置它们的依赖解决器。
IoC容器并不是魔法,它没有办法知道 Java 对象,除非你以某种方式通知它。当你调用 new 时,JVM 会实例化一个新对象的副本并直接交给你--它从不经过配置过程。有三种方法可以让你的 bean 得到配置。
注入你的 bean
直接将类全部交给 IoC 管理即可
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
Manual bean lookup: not recommended
@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;
}
}
@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);
}
}
其中还有一种使用 Configurable 注解,不过本地没有复现。猜测可能和版本有关。