← Назад к вопросам
Как Spring Boot запускает Bean в правильном порядке
2.4 Senior🔥 101 комментариев
#Spring Boot и Spring Data
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как Spring Boot запускает Bean в правильном порядке
Основная идея
Spring применяет граф зависимостей (dependency graph) для определения порядка инициализации Bean. Если Bean A зависит от Bean B, то B инициализируется ДО A. Spring автоматически анализирует конструкторы, поля и сеттеры для определения зависимостей.
Простой пример
@Component
public class DatabaseConnection {
public DatabaseConnection() {
System.out.println("DatabaseConnection создан");
}
}
@Component
public class UserRepository {
private DatabaseConnection db;
public UserRepository(DatabaseConnection db) {
System.out.println("UserRepository создан");
this.db = db;
}
}
@Component
public class UserService {
private UserRepository repo;
public UserService(UserRepository repo) {
System.out.println("UserService создан");
this.repo = repo;
}
}
// При запуске Spring Boot:
// DatabaseConnection создан <- 1-й
// UserRepository создан <- 2-й (зависит от DatabaseConnection)
// UserService создан <- 3-й (зависит от UserRepository)
Граф зависимостей
UserService
↓ зависит от
UserRepository
↓ зависит от
DatabaseConnection
Порядок инициализации (от листьев к корню):
1. DatabaseConnection (нет зависимостей)
2. UserRepository (зависит от DatabaseConnection)
3. UserService (зависит от UserRepository)
Алгоритм Spring для определения порядка
public class BeanDependencyResolver {
/*
1. Spring сканирует все классы с @Component, @Service, @Repository и т.д.
2. Для каждого Bean анализирует зависимости через:
- Конструктор (Constructor Injection) <- ПРЕДПОЧТИТЕЛЬНО
- Поля (@Autowired Field Injection)
- Сеттеры (Setter Injection)
3. Строит граф зависимостей (Directed Acyclic Graph, DAG)
4. Топологически сортирует (Topological Sort)
└─ Чтобы зависимости инициализировались перед зависимыми
5. Инициализирует в полученном порядке
*/
}
Пример: анализ зависимостей
@Configuration
public class AppConfig {
@Bean
public DatabaseConnection databaseConnection() {
System.out.println("[1] DatabaseConnection создан");
return new DatabaseConnection();
}
@Bean
public UserRepository userRepository(DatabaseConnection db) {
System.out.println("[2] UserRepository создан");
return new UserRepository(db);
}
@Bean
public UserService userService(UserRepository repo) {
System.out.println("[3] UserService создан");
return new UserService(repo);
}
@Bean
public UserController userController(UserService service) {
System.out.println("[4] UserController создан");
return new UserController(service);
}
}
// Spring анализирует сигнатуры методов:
// databaseConnection() -> зависит от: ничего
// userRepository(DatabaseConnection db) -> зависит от: DatabaseConnection
// userService(UserRepository repo) -> зависит от: UserRepository
// userController(UserService service) -> зависит от: UserService
// Порядок инициализации:
// [1] DatabaseConnection
// [2] UserRepository (параметр: DatabaseConnection)
// [3] UserService (параметр: UserRepository)
// [4] UserController (параметр: UserService)
Циклические зависимости (проблема)
// ❌ ОШИБКА: циклическая зависимость
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB; // ServiceA -> ServiceB
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA; // ServiceB -> ServiceA
// ServiceA -> ServiceB -> ServiceA -> ... бесконечный цикл!
}
// Spring выбросит ошибку:
// BeanCurrentlyInCreationException:
// Bean with name 'serviceA' has been circularly referenced
// Решение 1: @Lazy
@Component
public class ServiceB {
@Autowired
@Lazy // Отложенная инициализация
private ServiceA serviceA; // ServiceA инициализируется позже
}
// Решение 2: Setter Injection вместо Constructor
@Component
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) { // Сеттер
this.serviceA = serviceA;
}
}
Порядок инициализации: подробно
public class SpringBootInitializationOrder {
/*
1. Spring ApplicationContext создаёт граф Bean'ов
└─ Сканирует @ComponentScan пакеты
└─ Загружает @Bean методы из @Configuration классов
2. Анализирует зависимости
└─ Конструктор имеет приоритет (required dependency)
└─ Поля (@Autowired) анализируются
└─ Сеттеры анализируются
3. Проверяет циклические зависимости
└─ Если есть -> BeanCurrentlyInCreationException
4. Топологическая сортировка
└─ DFS (Depth-First Search) или BFS (Breadth-First Search)
└─ Результат: список Bean'ов в порядке инициализации
5. Инициализирует Bean'ы по очереди
Для каждого Bean:
a) Вызов конструктора (Constructor Injection)
b) Установка полей (@Autowired Field Injection)
c) Вызов сеттеров (Setter Injection)
d) @PostConstruct методы
e) InitializingBean.afterPropertiesSet()
f) Bean готов к использованию
*/
}
Жизненный цикл Bean
@Component
public class MyBean implements InitializingBean, DisposableBean {
// ✅ 1. Конструктор (создание объекта)
public MyBean() {
System.out.println("1. Конструктор вызван");
}
// ✅ 2. Dependency Injection (внедрение зависимостей)
@Autowired
private AnotherBean anotherBean; // Внедряется сюда
// ✅ 3. Инициализация
@PostConstruct
public void init() {
System.out.println("3. @PostConstruct");
// Здесь можно открыть соединение, загрузить кеш и т.д.
}
// Альтернатива @PostConstruct:
@Override
public void afterPropertiesSet() {
System.out.println("3b. afterPropertiesSet (InitializingBean)");
}
// ✅ 4. Bean готов к использованию
public void doSomething() {
System.out.println("4. Bean используется");
}
// ✅ 5. Завершение (шатдаун)
@PreDestroy
public void cleanup() {
System.out.println("5. @PreDestroy");
// Здесь закрыть соединение, сохранить состояние и т.д.
}
@Override
public void destroy() {
System.out.println("5b. destroy (DisposableBean)");
}
}
// Вывод при запуске Spring Boot:
// 1. Конструктор вызван
// 3. @PostConstruct
// 4. Bean используется
// 5. @PreDestroy (при выключении приложения)
Порядок внедрения зависимостей
@Component
public class MyService {
// Способ 1: Constructor Injection (ЛУЧШИЙ)
// Spring ищет параметр в конструкторе и внедряет его
private AnotherBean anotherBean;
public MyService(AnotherBean anotherBean) {
System.out.println("1. Constructor Injection");
this.anotherBean = anotherBean;
}
// Способ 2: Field Injection (не рекомендуется)
// @Autowired
// private AnotherBean anotherBean;
// Spring использует рефлексию для установки поля
// Минус: трудно тестировать, зависимость скрыта
// Способ 3: Setter Injection
// private AnotherBean anotherBean;
// @Autowired
// public void setAnotherBean(AnotherBean anotherBean) {
// this.anotherBean = anotherBean;
// }
}
// Порядок выбора Spring:
// 1. Constructor Injection (если есть конструктор с параметрами)
// 2. Field Injection (@Autowired на поля)
// 3. Setter Injection (@Autowired на сеттеры)
Пример с условными Bean'ами
@Configuration
public class ConditionalBeanConfig {
@Bean
@ConditionalOnProperty(name = "app.db.type", havingValue = "postgres")
public DatabaseConnection postgresConnection() {
System.out.println("Postgres Bean создан");
return new PostgresConnection();
}
@Bean
@ConditionalOnProperty(name = "app.db.type", havingValue = "mysql")
public DatabaseConnection mysqlConnection() {
System.out.println("MySQL Bean создан");
return new MySQLConnection();
}
@Bean
public UserRepository userRepository(DatabaseConnection db) {
System.out.println("UserRepository создан");
return new UserRepository(db);
}
}
// Если app.db.type=postgres:
// Postgres Bean создан <- DatabaseConnection
// UserRepository создан <- зависит от DatabaseConnection
// Если app.db.type=mysql:
// MySQL Bean создан <- DatabaseConnection
// UserRepository создан <- зависит от DatabaseConnection
Можно ли контролировать порядок явно?
@Configuration
public class ExplicitOrderConfig {
@Bean
public ServiceA serviceA() {
return new ServiceA();
}
@Bean
public ServiceB serviceB(ServiceA serviceA) { // Явная зависимость
return new ServiceB(serviceA);
}
// Альтернатива: @Lazy и ObjectProvider
@Bean
public ServiceC serviceC(ObjectProvider<ServiceB> serviceBProvider) {
// serviceBProvider.getIfAvailable() вернёт ServiceB или null
// Даёт контроль над моментом инициализации
return new ServiceC(serviceBProvider.getIfAvailable());
}
}
// @Depends аннотация (редко)
@Bean(name = "serviceD", dependsOn = {"serviceA", "serviceB"})
public ServiceD serviceD() {
return new ServiceD();
// Spring гарантирует, что serviceA и serviceB инициализируются первыми
}
Итого: правильный порядок инициализации
- Spring анализирует зависимости через конструкторы, поля и сеттеры
- Строит граф зависимостей (DAG — Directed Acyclic Graph)
- Топологически сортирует Bean'ы (зависимости перед зависимыми)
- Инициализирует по порядку:
- Конструктор → Field Injection → Setter Injection → @PostConstruct
- Обнаруживает циклические зависимости и выбрасывает ошибку
Порядок в 99% случаев правильный, если вы используете Constructor Injection (это лучшая практика).