1. 概述
在这个教程中,我们将探讨导致NullPointerException
的一些常见错误,这些错误发生在自动注入(Autowired)字段上。我们还将解释如何解决这些问题。
2. 问题描述
首先,定义一个Spring组件,其中有一个空的doWork
方法:
@Component
public class MyComponent {
public void doWork() {}
}
接着,定义我们的服务类。我们将利用Spring的功能,在服务类中注入一个MyComponent
bean,以便在服务方法中调用doWork
方法:
public class MyService {
@Autowired
MyComponent myComponent;
public String serve() {
myComponent.doWork();
return "success";
}
}
现在,添加一个控制器,它将实例化一个服务并调用serve
方法:
@Controller
public class MyController {
public String control() {
MyService userService = new MyService();
return userService.serve();
}
}
乍看之下,我们的代码似乎没有问题。然而,运行应用后,调用控制器的控制方法会导致以下异常:
java.lang.NullPointerException: null
at com.baeldung.autowiring.service.MyService.serve(MyService.java:14)
at com.baeldung.autowiring.controller.MyController.control(MyController.java:12)
这里发生了什么?当我们控制器中的MyService
构造函数被调用时,我们创建了一个不受Spring管理的对象。由于Spring对这个MyService
对象的存在一无所知,它无法在其内部注入MyComponent
bean。因此,我们在创建的MyService
对象内部的MyComponent
实例将保持为null,当我们尝试在这个对象上调用方法时,就会引发NullPointerException
。
3. 解决方案
要解决这个问题,我们需要确保在控制器中使用的MyService
实例是Spring管理的Bean。
首先,告诉Spring为MyService
类生成一个Bean。我们可以采用多种方式实现,最简单的方法是在MyService
类上添加@Component
注解或其衍生注解。例如,可以这样做:
@Service
public class MyService {
@Autowired
MyComponent myComponent;
public String serve() {
myComponent.doWork();
return "success";
}
}
另一种达到相同目标的方法是在@Configuration
文件中添加一个@Bean
方法:
@Configuration
public class MyServiceConfiguration {
@Bean
MyService myService() {
return new MyService();
}
}
但是,仅仅将MyService
类转换为Spring管理的bean还不够。现在,我们需要在控制器中自动注入它,而不是直接使用new
关键字创建。以下是修复后的控制器代码:
@Controller
public class MyController {
@Autowired
MyService myService;
public String control() {
return myService.serve();
}
}
现在,调用控制方法将如预期返回serve
方法的结果。
4. 总结
在这篇文章中,我们看到了一个常见的错误,当我们无意中混合Spring注入和通过构造函数创建的对象时,这可能导致NullPointerException
。我们通过避免这种责任混杂,并将原本自己管理的对象转换为Spring管理的Bean来解决了问题。
如往常一样,代码可以在GitHub上找到。