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

Что происходит в Spring, когда делаешь первый запрос

1.7 Middle🔥 131 комментариев
#Stream API и функциональное программирование#Многопоточность

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

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

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

Что происходит в Spring при первом запросе

Первый HTTP запрос в Spring приложении инициирует сложный процесс инициализации, который включает создание контекста, регистрацию обработчиков и конфигурацию. Это критичный момент для понимания performance приложения.

Жизненный цикл Spring при первом запросе

1. Client отправляет HTTP запрос
2. DispatcherServlet перехватывает
3. ApplicationContext инициализируется (если ещё не создан)
4. Beans создаются (если ленивая инициализация)
5. HandlerMapping ищет controller
6. Controller выполняется
7. Response отправляется

1. DispatcherServlet инициализация

public class DispatcherServlet extends FrameworkServlet {
    @Override
    protected void service(HttpServletRequest request, 
                          HttpServletResponse response) {
        // Первый запрос
        long startTime = System.nanoTime();
        
        try {
            doDispatch(request, response);
        } finally {
            // Логирование времени выполнения
        }
    }
    
    protected void doDispatch(HttpServletRequest request, 
                             HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        
        // 1. Поиск подходящего Handler
        mappedHandler = getHandler(processedRequest);
        
        if (mappedHandler == null) {
            sendDefaultResponse(request, response);  // 404
            return;
        }
        
        // 2. Получение HandlerAdapter
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
        // 3. Выполнение pre-handles (interceptors)
        for (HandlerInterceptor interceptor : mappedHandler.getInterceptors()) {
            if (!interceptor.preHandle(request, response, 
                                      mappedHandler.getHandler())) {
                return;
            }
        }
        
        // 4. Выполнение handler (controller method)
        ModelAndView mv = ha.handle(processedRequest, response, 
                                    mappedHandler.getHandler());
        
        // 5. Выполнение post-handles
        for (HandlerInterceptor interceptor : mappedHandler.getInterceptors()) {
            interceptor.postHandle(request, response, 
                                  mappedHandler.getHandler(), mv);
        }
        
        // 6. Рендеринг view
        processDispatchResult(request, response, mappedHandler, mv, null);
    }
}

2. ApplicationContext инициализация

// Если используется ленивая инициализация (lazy init)
public class ApplicationContextInitialization {
    
    // Первый запрос может инициализировать контекст
    private ApplicationContext createApplicationContext() {
        // Spring сканирует:
        // 1. @Configuration классы
        // 2. @Component, @Service, @Repository, @Controller
        // 3. Создаёт bean definitions
        // 4. Разрешает зависимости
        
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext("com.example");
        // На этом этапе:
        // - Все @Bean методы выполняются
        // - Все singleton beans создаются
        // - Autowiring происходит
        // - PostProcessors выполняются
        
        return context;
    }
    
    // Это может занять 1-10+ секунд (cold start)
}

3. Bean Creation (если ещё не создан)

public class BeanCreationProcess {
    
    // Типичный контроллер
    @RestController
    @RequestMapping("/api")
    public class UserController {
        
        @Autowired
        private UserService userService;  // Injection при первом использовании
        
        @Autowired
        private UserRepository userRepository;
        
        @GetMapping("/users/{id}")
        public User getUser(@PathVariable Long id) {
            // При первом запросе выполняются все инициализации
            return userService.findById(id);
        }
    }
    
    // Процесс создания bean:
    // 1. Spring проверяет, есть ли bean UserController
    // 2. Если нет, создаёт новый экземпляр
    // 3. Проверяет @Autowired зависимости
    // 4. Создаёт UserService (если нужен)
    // 5. Создаёт UserRepository
    // 6. Inject зависимости в controller
    // 7. Вызывает @PostConstruct методы
}

4. Handler Mapping

public class HandlerMappingProcess {
    
    // Spring сканирует классы с @RequestMapping
    @RestController
    public class UserController {
        
        @GetMapping("/users/{id}")
        public User getUser(@PathVariable Long id) {
            return new User(id);
        }
        
        @PostMapping("/users")
        public User createUser(@RequestBody UserRequest request) {
            return new User();
        }
    }
    
    // RequestMappingHandlerMapping регистрирует:
    // GET /users/{id} -> UserController::getUser
    // POST /users -> UserController::createUser
    // 
    // При первом запросе это может быть динамически построено
}

5. Параметр инициализации и кэширование

public class RequestParameterHandling {
    
    @GetMapping("/search")
    public List<User> search(
        @RequestParam String query,
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(required = false) String sort) {
        
        // Spring должен конвертировать параметры:
        // - String to int для page
        // - Валидировать required параметры
        // - Применять дефолтные значения
        
        // Это может быть кэшировано на следующие запросы
        return Collections.emptyList();
    }
}

6. Полная последовательность первого запроса

public class CompleteFirstRequestFlow {
    
    /*
    T0 - Client отправляет GET /api/users/1
    
    T0 + 0ms:
    1. DispatcherServlet.service() вызывается
    2. Проверяется init флаг
    3. Если не инициализирован, вызывается init()
    
    T0 + 5ms:
    4. ApplicationContext создан (если lazy)
    5. Все beans созданы
    6. PostProcessors выполнены
    
    T0 + 10ms:
    7. RequestMappingHandlerMapping.getHandler() вызывается
    8. Ищется match для /api/users/1
    9. Находится UserController::getUser
    
    T0 + 12ms:
    10. ArgumentResolvers решают параметры
    11. @PathVariable Long id разрешается в 1
    12. ReturnValueHandlers регистрируются
    
    T0 + 15ms:
    13. UserController.getUser(1) выполняется
    14. Вызывается userService.findById(1)
    15. Выполняется SQL query или cache lookup
    
    T0 + 25ms:
    16. View resolution (если REST, просто JSON)
    17. HttpMessageConverter serializes ответ
    18. Response отправляется
    
    T0 + 26ms:
    19. Логируется: "GET /api/users/1 200 OK в 26ms"
    */
}

7. Performance оптимизация

// ПЛОХО: ленивая инициализация при первом запросе
@SpringBootApplication
public class SlowApp {
    // Контекст создаётся при первом запросе
    // Первый запрос может быть очень медленным (5-30 сек)
}

// ХОРОШО: ранняя инициализация
@SpringBootApplication
public class FastApp {
    public static void main(String[] args) {
        // Spring Boot инициализирует контекст сразу
        // При запуске (не при первом запросе)
        SpringApplication.run(FastApp.class, args);
        
        // Первый запрос будет быстрым (~50ms)
    }
}

8. Что происходит под капотом

public class UnderTheHood {
    
    public void firstRequest() {
        // 1. Томкат получает запрос
        // 2. Tomcat создаёт HttpServletRequest
        // 3. Tomcat создаёт HttpServletResponse
        
        // 4. DispatcherServlet.service() вызывается
        // 5. FrameworkServlet.service() вызывается
        // 6. Проверка: инициализирован ли контекст?
        
        if (!initialized) {
            // 7a. Инициализация контекста
            initServletBean();  // Очень медленно
            
            // 7b. Кэширование настроек
            cacheHandlerMethods();
            cacheArgumentResolvers();
            cacheReturnValueHandlers();
        }
        
        // 8. doDispatch() - обработка запроса
        doDispatch();
    }
}

9. Времени выполнения типичного Spring приложения

Cold Start (первый запрос)
├── ApplicationContext init     500-2000ms
├── Bean creation              1000-5000ms
├── Handler mapping            100-500ms
├── Controller execution       10-100ms
├── Response serialization     5-50ms
└── Total                      1-10 seconds

Warm Start (последующие запросы)
├── Handler lookup (cached)    0.1ms
├── Argument resolution        0.5ms
├── Controller execution       10-100ms
├── Response serialization     5-50ms
└── Total                      15-150ms

10. Spring Boot optimization

@SpringBootApplication
public class OptimizedApp {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(OptimizedApp.class);
        
        // Отключить ненужные auto-configurations
        app.setAdditionalProfiles("production");
        
        // Ранее создание контекста
        app.run(args);
        
        // Измерить time-to-ready
        // Обычно 3-10 секунд для modern Spring приложения
    }
}

Заключение

Первый запрос в Spring приложении инициирует:

  1. ApplicationContext инициализация — может быть медленной
  2. Bean creation и dependency injection — сканирование и создание
  3. HandlerMapping регистрация — сопоставление URL с контроллерами
  4. ArgumentResolver кэширование — преобразование параметров
  5. Controller execution — обработка бизнес-логики
  6. Response serialization — конвертация в JSON/XML

Второй и последующие запросы выполняются значительно быстрее, так как всё кэшируется. Это критично при работе с контейнерами и serverless функциями, где cold start имеет значение.