← Назад к вопросам

Что происходит при подъеме контекста

2.0 Middle🔥 201 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что происходит при подъеме контекста в 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 с разрешением всех зависимостей и вызова методов инициализации. Это происходит автоматически и незаметно, но понимание этого процесса критично для отладки проблем.