java - java - 为什么我的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)


. . .



时间:

注释 @Autowirednull,因为 spring 不知道你使用 new 创建的MileageFeeCalculator 副本,并且不知道如何自动装配它。

控制( IoC ) 容器的spring 反转有三个主要的逻辑组件: 组件可以通过关联来使用,并在上下文中查看多个不同bean的依赖关系,以及在必要的顺序中对它的进行实例化并确定如何进行配置的依赖关系。

IoC容器并不是魔术,它没有方法了解Java对象,除非你通知它们。 调用 new 时,JVM会实例化新对象的副本并直接将它交给 you--it,永远不会通过配置过程。 有三种方法可以让你的bean配置。

这部分代码都是使用 Spring Boot 发布的,在中发布,你可以查看完整的项目,查看所有需要让它工作的方法。NullPointerException标签:nonworking

插入你的bean

最理想的选择是让 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);


 }


}



如果需要为不同的请求创建服务对象的新实例,则仍然可以使用的spring bean范围来使用注入。

通过注入 @MileageFeeCalculator 服务对象来工作的标记:working-inject-bean

使用 @Configurable

在使用 new 创建的对象时,你可以使用 spring @Configurable 注释和AspectJ编译时编织工具来插入对象。 这种方法将代码插入到对象的构造函数中,以通知 spring 正在创建它,以便 spring 可以配置新实例。 这需要在构建( 例如使用 ajc 编译) 中配置一些配置,并打开 spring 配置处理器( 带有JavaConfig语法的@EnableSpringConfigured )的运行时。 Roo系统使用这种方法来允许实体的实例获得所需的持久化信息。


@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 应用程序上下文。

要实现这里目的,你需要一个类,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

你的问题是新的( 用java风格创建对象)


MileageFeeCalculator calc = new MileageFeeCalculator();



使用注释 @Service@Component@Configuration bean是在
服务器启动时 spring的应用程序上下文。 但是当使用新操作符创建对象时,对象没有在已经创建的应用程序上下文中注册。 例如我使用的Employee.java 类。

试试这个:


public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {



@Override


public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {


 String name ="tenant";


 System.out.println("Bean factory post processor is initialized"); 


 beanFactory.registerScope("employee", new Employee());



 Assert.state(beanFactory instanceof BeanDefinitionRegistry,


"BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");


 BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;



 for (String beanName : beanFactory.getBeanDefinitionNames()) {


 BeanDefinition definition = beanFactory.getBeanDefinition(beanName);


 if (name.equals(definition.getScope())) {


 BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);


 registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());


 }


 }


}



}



...