1. Обзор
В этом руководстве мы рассмотрим распространенные ошибки, которые приводят к исключению NullPointerException
в поле Autowired
. Мы также объясним, как решить проблему.
2. Постановка задачи
Во-первых, давайте определим компонент Spring с пустым методом doWork
:
@Component
public class MyComponent {
public void doWork() {}
}
Затем давайте определим наш класс обслуживания. Мы будем использовать возможности Spring для внедрения bean-компонента MyComponent
внутри нашего сервиса, чтобы мы могли вызывать метод 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.foreach.autowiring.service.MyService.serve(MyService.java:14)
at com.foreach.autowiring.controller.MyController.control(MyController.java:12)
Что здесь случилось? Когда мы вызвали конструктор MyService
в нашем контроллере, мы создали объект, который не управляется Spring. Не зная о существовании этого объекта MyService
, Spring не может внедрить в него bean-компонент MyComponent
. Таким образом, экземпляр MyComponent
внутри созданного нами объекта MyService останется нулевым, вызывая исключение NullPointerException
, которое мы получаем, когда пытаемся вызвать метод для этого объекта.
3. Решение
Чтобы решить эту проблему, мы должны сделать экземпляр MyService
, используемый в нашем контроллере, компонентом, управляемым Spring.
Во-первых, давайте скажем Spring сгенерировать Bean для нашего класса MyService
. У нас есть различные возможности для этого. Самый простой — украсить класс MyService аннотацией
@Component
или любой из ее производных. Например, мы могли бы сделать следующее:
@Service
public class MyService {
@Autowired
MyComponent myComponent;
public String serve() {
myComponent.doWork();
return "success";
}
}
Другой альтернативой для достижения той же цели является добавление метода @Bean в файл
@Configuration
:
@Configuration
public class MyServiceConfiguration {
@Bean
MyService myService() {
return new MyService();
}
}
Однако превращения класса MyService
в bean-компонент, управляемый Spring, недостаточно. Теперь мы должны автоматически связать его внутри нашего контроллера, вместо того, чтобы вызывать для него new
. Посмотрим, как выглядит исправленная версия контроллера:
@Controller
public class MyController {
@Autowired
MyService myService;
public String control() {
return myService.serve();
}
}
Теперь вызов метода управления вернет результат метода serve
, как и ожидалось.
4. Вывод
В этой статье мы увидели очень распространенную ошибку, которая может вызвать исключение NullPointerException
, когда мы непреднамеренно смешиваем внедрение Spring с объектами, которые мы создаем, вызывая их конструкторы. Мы исправили проблему, избежав этой ответственности mic-mac, и превратили объект, который мы использовали для управления собой, в компонент, управляемый Spring.
Как всегда, код доступен на GitHub .