Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что происходит при подъеме контекста в Spring
Подъём контекста (Context Startup) — это процесс инициализации всего Spring приложения. Это критический момент, где создаются все Bean, выполняются конфигурации и приложение готовится к работе. Давайте разберём все этапы подробно.
1. Инициализация ApplicationContext
public class ContextStartup {
public static void main(String[] args) {
// Создание контекста — начало подъёма
ApplicationContext context = SpringApplication.run(Application.class, args);
// После этой строки контекст полностью инициализирован
}
}
Внутри происходит создание одного из типов контекста:
// В Spring Boot автоматически выбирается нужный
ApplicationContext context = new AnnotationConfigApplicationContext();
// или для веб-приложений
ServletWebServerApplicationContext context = new ServletWebServerApplicationContext();
2. Фазы подъёма контекста
Фаза 1: Подготовка к запуску
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
// 1.1 Создание слушателей
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(); // Вызываются ApplicationStartingEvent
// 1.2 Создание ApplicationContext
ConfigurableApplicationContext context = createApplicationContext();
return context;
}
}
Фаза 2: Создание BeanFactory и загрузка конфигурации
public class AbstractApplicationContext {
public void refresh() {
// 2.1 Создание BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 2.2 Регистрация BeanFactoryPostProcessor
// Это позволяет модифицировать BeanDefinition перед созданием Bean
invokeBeanFactoryPostProcessors(beanFactory);
}
}
Фаза 3: Сканирование и регистрация Bean
@ComponentScan(basePackages = "com.example")
public class Application {}
// Spring выполняет:
// 1. Сканирование classpath
// 2. Поиск @Component, @Service, @Repository, @Controller
// 3. Создание BeanDefinition для каждого найденного класса
// 4. Регистрация в BeanDefinitionRegistry
Фаза 4: Создание экземпляров Bean (Instantiation)
public class DefaultListableBeanFactory {
public void preInstantiateSingletons() {
// 4.1 Для каждого singleton Bean
for (String beanName : beanNames) {
BeanDefinition bd = getBeanDefinition(beanName);
// 4.2 Если не lazy-init
if (!bd.isLazyInit()) {
// 4.3 Вызов getBean() для создания
getBean(beanName);
}
}
}
}
Фаза 5: Разрешение зависимостей (Dependency Resolution)
public class DefaultListableBeanFactory {
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 5.1 Создание экземпляра (Instantiation)
Object instance = createBeanInstance(beanName, mbd, args);
// 5.2 Внедрение зависимостей (Dependency Injection)
populateBean(beanName, mbd, instanceWrapper);
// 5.3 Инициализация Bean
Object exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
}
3. Жизненный цикл одного Bean при подъёме
@Component
public class ExampleBean implements BeanNameAware, BeanFactoryAware,
InitializingBean, DisposableBean {
// Этап 1: Создание экземпляра
public ExampleBean() {
System.out.println("1. Constructor called");
}
// Этап 2: Установка свойств через Setter Injection
@Autowired
private Dependency dependency;
// Этап 3: BeanNameAware.setBeanName() вызывается
@Override
public void setBeanName(String name) {
System.out.println("2. setBeanName: " + name);
}
// Этап 4: BeanFactoryAware.setBeanFactory() вызывается
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("3. setBeanFactory called");
}
// Этап 5: BeanPostProcessor.postProcessBeforeInitialization()
// Вызывается ДО инициализации
// Этап 6: @PostConstruct аннотированный метод
@PostConstruct
public void init() {
System.out.println("4. PostConstruct - init() called");
}
// Этап 7: InitializingBean.afterPropertiesSet() вызывается
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("5. afterPropertiesSet called");
}
// Этап 8: BeanPostProcessor.postProcessAfterInitialization()
// Вызывается ПОСЛЕ инициализации
// Здесь создаются CGLIB прокси для @Transactional, @Async и т.д.
// Этап 9: Bean готов к использованию
public void doWork() {
System.out.println("9. Bean is ready to use");
}
// Этап 10: При завершении контекста
@PreDestroy
public void cleanup() {
System.out.println("10. PreDestroy - cleanup() called");
}
@Override
public void destroy() throws Exception {
System.out.println("11. destroy called");
}
}
Порядок вызова при подъёме контекста:
1. Constructor
2. setBeanName
3. setBeanFactory
4. PostConstruct (if exists)
5. afterPropertiesSet (if implements InitializingBean)
6. Bean готов к использованию
4. Обработка конфигураций (@Configuration)
@Configuration
public class AppConfig {
// @Configuration класс сам становится Bean
@Bean
public UserRepository userRepository() {
return new UserRepository();
}
@Bean
public UserService userService(UserRepository userRepository) {
// Spring автоматически внедряет зависимость
return new UserService(userRepository);
}
}
// Порядок:
// 1. AppConfig класс создаётся как Bean
// 2. Методы с @Bean вызываются
// 3. Spring анализирует параметры метода и внедряет нужные Bean
5. Event Listeners при подъёме
@Component
public class ApplicationStartupListener {
@EventListener(ApplicationContextEvent.class)
public void onApplicationEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
System.out.println("Контекст полностью инициализирован");
}
}
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
System.out.println("Приложение готово к обработке запросов");
}
}
6. Быстрая диагностика при подъёме контекста
// Добавить в application.properties
logging.level.org.springframework=DEBUG
logging.level.org.springframework.boot=DEBUG
// Или через код
@Component
public class ContextMonitor {
@Autowired
private ConfigurableApplicationContext context;
@PostConstruct
public void init() {
System.out.println("Всего Bean в контексте: " + context.getBeanDefinitionCount());
for (String beanName : context.getBeanDefinitionNames()) {
System.out.println(" - " + beanName);
}
}
}
7. Частые проблемы при подъёме
// Проблема 1: Циклическая зависимость
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB; // ServiceB зависит от ServiceA
}
// Проблема 2: Bean не найден
@Service
public class UserService {
@Autowired
private NonExistentService service; // NoSuchBeanDefinitionException
}
// Проблема 3: @Autowired для interface с несколькими реализациями
@Service
public class OrderService {
@Autowired
private PaymentProcessor processor; // Какая реализация?
// Решение: @Qualifier("creditCard") или @Primary
}
В итоге: При подъёме контекста Spring выполняет огромную работу — от сканирования классов до создания Bean с разрешением всех зависимостей и вызова методов инициализации. Это происходит автоматически и незаметно, но понимание этого процесса критично для отладки проблем.