Почему Spring @Autowired поле равно null и как это исправить?
Основная причина: Spring не управляет вашим объектом
Проблема в том, что UserController создаётся вами вручную (через new UserController()), а не через контейнер Spring. Аннотация @Autowired работает только с бинами, которыми управляет Spring IoC-контейнер. Если вы создаёте объект сами, Spring не имеет возможности внедрить зависимости, и поле остаётся null.
Как это исправить?
Есть три основных подхода. Рассмотрим каждый с примерами.
1. Сделайте контроллер бином Spring
Добавьте аннотацию @Component (или @Controller для веб-слоя) и получайте объект из контекста:
@Controller // или @Component
public class UserController {
@Autowired
private UserService userService;
public void handleRequest() {
userService.getUser(1); // Теперь работает
}
}
// В точке входа:
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApp.class, args);
UserController controller = context.getBean(UserController.class);
controller.handleRequest();
}
2. Используйте внедрение через конструктор (рекомендуется)
Этот подход считается лучшей практикой — он делает зависимости явными и упрощает тестирование:
@Controller
public class UserController {
private final UserService userService;
// Spring автоматически внедрит зависимость через конструктор
public UserController(UserService userService) {
this.userService = userService;
}
public void handleRequest() {
userService.getUser(1);
}
}
При использовании одного конструктора @Autowired можно опустить — Spring сам его найдёт.
3. Проверьте настройки компонентного сканирования
Убедитесь, что ваши классы находятся в пакетах, которые сканирует Spring. По умолчанию Spring Boot сканирует пакет, где находится класс с @SpringBootApplication, и все вложенные пакеты. Если ваш контроллер лежит в другом пакете, добавьте @ComponentScan:
@SpringBootApplication
@ComponentScan("com.example.controllers") // Укажите ваш пакет
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Дополнительные советы
- Не создавайте бины через
new— всегда получайте их из контекста или внедряйте через DI. - Проверьте логи — Spring пишет, какие бины созданы. Ищите сообщения вида
"Creating bean 'userController'". - Используйте конструктор — это позволяет сделать поля
finalи избежать мутабельности. - Для тестов используйте
@SpringBootTestи@Autowiredпрямо в тестовом классе.
Почему это происходит?
Spring — это контейнер, который управляет жизненным циклом бинов. Когда вы пишете @Autowired, вы говорите: «Spring, найди подходящий бин и вставь его сюда». Но если объект не является бином (не аннотирован и не создан контейнером), Spring просто игнорирует аннотацию, и поле остаётся null. Это классическая ошибка новичков — смешивать ручное создание объектов с DI.
Альтернативный подход: Использование ApplicationContext напрямую
Если по каким-то причинам вы не можете сделать класс бином (например, это legacy-код), можно получить бин вручную через ApplicationContext. Но это считается антипаттерном — используйте только в крайнем случае.
public class UserController {
private UserService userService;
public UserController() {
// Получаем контекст Spring
ApplicationContext context = SpringContextHolder.getContext();
this.userService = context.getBean(UserService.class);
}
public void handleRequest() {
userService.getUser(1);
}
}
// Вспомогательный класс для хранения контекста
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
Когда это может пригодиться?
- В утилитных классах, которые не могут быть бинами.
- При интеграции со сторонними библиотеками, которые создают объекты через рефлексию.
- В редких случаях миграции старого кода.
Однако помните: такой код сложнее тестировать, он создаёт скрытые зависимости и нарушает принцип инверсии контроля. Лучше всегда стремиться к тому, чтобы ваши классы были бинами Spring.