Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое HandlerAdapter (в Spring MVC)
Определение
HandlerAdapter — это компонент Spring MVC, который адаптирует различные типы handlers (обработчиков) к единому интерфейсу, который может использовать DispatcherServlet. Он позволяет Spring работать с разными типами контроллеров.
Проблема, которую решает HandlerAdapter
Если бы HandlerAdapter не существовал, DispatcherServlet должен был бы знать о всех возможных типах handlers:
Без HandlerAdapter:
┌──────────────────────────────────────────┐
│ DispatcherServlet │
├──────────────────────────────────────────┤
│ if (handler instanceof Controller) │
│ → вызови execute() │
│ else if (handler instanceof HttpServlet) │
│ → вызови service() │
│ else if (handler instanceof Function) │
│ → вызови apply() │
│ else if (handler instanceof ...) │
│ → ??? │
└──────────────────────────────────────────┘
Код усложняется, нарушается Open/Closed Principle!
С HandlerAdapter:
С HandlerAdapter (паттерн Adapter):
┌──────────────────────────────────────────┐
│ DispatcherServlet │
├──────────────────────────────────────────┤
│ adapter = handlerAdapterRegistry │
│ .getAdapter(handler) │
│ adapter.handle(request, response) │
└──────────────────────────────────────────┘
↓
Один унифицированный интерфейс!
Иерархия HandlerAdapter в Spring
public interface HandlerAdapter {
// Проверяет, может ли этот адаптер обработать handler
boolean supports(Object handler);
// Выполняет handler и возвращает ModelAndView
ModelAndView handle(
HttpServletRequest request,
HttpServletResponse response,
Object handler
) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
Типы HandlerAdapter в Spring
1. SimpleControllerHandlerAdapter — для старых Controller интерфейсов
// Старый стиль (pre-annotation, редко используется)
public class LegacyController implements Controller {
@Override
public ModelAndView handleRequest(
HttpServletRequest request,
HttpServletResponse response) throws Exception {
return new ModelAndView("myView");
}
}
// SimpleControllerHandlerAdapter отвечает за обработку таких контроллеров
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller); // Проверяем тип
}
@Override
public ModelAndView handle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
Controller controller = (Controller) handler;
return controller.handleRequest(request, response);
}
}
2. RequestMappingHandlerAdapter — для @RequestMapping (основной)
// Современный стиль (используется по умолчанию)
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return new User(id, "John");
}
@PostMapping
public User createUser(@RequestBody User user) {
return user;
}
}
// RequestMappingHandlerAdapter обрабатывает эти контроллеры
// Он:
// 1. Парсит @PathVariable, @RequestParam, @RequestBody
// 2. Вызывает метод с правильными аргументами
// 3. Сериализует результат в JSON/XML
public class RequestMappingHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HandlerMethod);
}
@Override
public ModelAndView handle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// Парсим аргументы
Object[] args = extractMethodArguments(request, handlerMethod);
// Вызываем метод
Object returnValue = handlerMethod.getMethod()
.invoke(handlerMethod.getBean(), args);
// Сериализуем результат
return handleReturnValue(returnValue, response);
}
}
3. HttpRequestHandlerAdapter — для HttpRequestHandler
// Для компонентов, реализующих HttpRequestHandler
public class FileDownloadHandler implements HttpRequestHandler {
@Override
public void handleRequest(
HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/pdf");
// Скачиваем файл напрямую
}
}
// HttpRequestHandlerAdapter
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
public ModelAndView handle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpRequestHandler httpHandler = (HttpRequestHandler) handler;
httpHandler.handleRequest(request, response);
return null; // Нет view, только ответ
}
}
Как работает HandlerAdapter
1. HTTP Request приходит
↓
2. DispatcherServlet получает request
↓
3. HandlerMapping находит handler (метод контроллера)
↓
4. DispatcherServlet ищет подходящий HandlerAdapter
↓
5. HandlerAdapter.supports(handler) → true
↓
6. HandlerAdapter.handle() выполняет handler
↓
7. Возвращается ModelAndView
↓
8. DispatcherServlet отправляет ответ клиенту
Практический пример
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
// Кастомный тип handler
interface CustomHandler {
String execute();
}
// Реализация
public class MyCustomHandler implements CustomHandler {
@Override
public String execute() {
return "Hello from custom handler";
}
}
// Кастомный HandlerAdapter
public class CustomHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return handler instanceof CustomHandler;
}
@Override
public ModelAndView handle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
CustomHandler customHandler = (CustomHandler) handler;
String result = customHandler.execute();
// Пишем результат напрямую в response
response.getWriter().write(result);
return null; // Нет view
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
}
// Конфигурация
@Configuration
public class DispatcherServletConfig {
@Bean
public CustomHandlerAdapter customHandlerAdapter() {
return new CustomHandlerAdapter();
}
}
Реальный пример: RequestMappingHandlerAdapter работает так
// При этом запросе:
// GET /api/users/123?sort=name
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(
@PathVariable Long id, // Из URL
@RequestParam String sort) { // Из query параметров
return new User(id, "John", sort);
}
}
// RequestMappingHandlerAdapter:
// 1. Распознает @PathVariable → парсит из URL
// 2. Распознает @RequestParam → парсит из query
// 3. Вызывает метод с правильными значениями
// 4. Получает User объект
// 5. Сериализует в JSON и отправляет
Цепь HandlerAdapter
Spring пробует адаптеры по порядку:
@Bean
public CompositeHandlerAdapter compositeHandlerAdapter() {
List<HandlerAdapter> adapters = Arrays.asList(
new RequestMappingHandlerAdapter(), // Пробует первым (аннотации)
new SimpleControllerHandlerAdapter(), // Потом (старые Controller)
new HttpRequestHandlerAdapter(), // Потом (HttpRequestHandler)
new CustomHandlerAdapter() // В конце (кастомные)
);
for (HandlerAdapter adapter : adapters) {
if (adapter.supports(handler)) {
return adapter; // Выбирает первый подходящий
}
}
throw new ServletException("No adapter for handler");
}
Лучшие практики
- Используй @RequestMapping вместо старого Controller интерфейса
// ✅ Хорошо
@RestController
public class MyController {
@GetMapping("/users")
public List<User> getUsers() { ... }
}
// ❌ Плохо (старый стиль)
public class MyController implements Controller {
public ModelAndView handleRequest(...) { ... }
}
- Кастомные HandlerAdapter для специфичных нужд
// Если нужна нестандартная обработка запросов
public class AuthHandlerAdapter implements HandlerAdapter { ... }
Итоговый вывод
HandlerAdapter — это ключевый компонент Spring MVC, который реализует паттерн Adapter для работы с разными типами handlers. Он позволяет Spring быть гибким и расширяемым, поддерживая как встроенные типы handlers (аннотированные контроллеры, старые Controller интерфейсы), так и кастомные при необходимости. Для обычного разработчика Spring это работает "за сценой", но понимание этого механизма важно для advanced scenarios и оптимизации.