Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
HandlerMapping в Spring Framework
Что это такое
HandlerMapping — это интерфейс в Spring Framework, который отвечает за маппинг входящих HTTP запросов на обработчики (handlers). Это один из ключевых компонентов DispatcherServlet, который определяет, какой контроллер должен обработать конкретный запрос.
Назначение
- Сопоставление URL с контроллерами — найти нужный обработчик для запроса
- Поддержка различных стратегий маппинга — разные типы маршрутизации
- Кэширование — оптимизация производительности
- Перехват запросов — обработка перехватчиков до попадания в контроллер
Как это работает
HTTP Request
↓
DispatcherServlet
↓
HandlerMapping.getHandler(request)
↓
HandlerExecutionChain (Handler + Interceptors)
↓
ControllerMethod
Главные реализации HandlerMapping
1. RequestMappingHandlerMapping
Самая часто используемая — маппит URL на методы с аннотацией @RequestMapping:
@RestController
@RequestMapping("/api/users")
public class UserController {
// Маппится на GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// Маппится на GET /api/users/{id}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.findById(id);
}
// Маппится на POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
// Маппится на PUT /api/users/{id}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
// Маппится на DELETE /api/users/{id}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
}
2. SimpleUrlHandlerMapping
Паттерн-базированное маппирование через конфигурацию:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping(
UserController userController) {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
Map<String, Object> urlMap = new HashMap<>();
urlMap.put("/users", userController);
urlMap.put("/products", productController);
urlMap.put("/orders", orderController);
mapping.setUrlMap(urlMap);
mapping.setOrder(0);
return mapping;
}
}
3. BeanNameUrlHandlerMapping
Маппирует URL на имена beans в контексте:
@Configuration
public class AppConfig {
// Bean с именем /home маппится на URL /home
@Bean("/home")
public Controller homeController() {
return new HomeController();
}
@Bean("/about")
public Controller aboutController() {
return new AboutController();
}
}
HandlerExecutionChain
Результат работы HandlerMapping включает обработчик и перехватчики:
public class HandlerMappingExample {
// HandlerMapping возвращает:
// HandlerExecutionChain {
// handler: UserController.getUserById
// interceptors: [LoggingInterceptor, SecurityInterceptor]
// }
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// Добавляем перехватчики
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/**");
registry.addInterceptor(new SecurityInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/public/**");
}
}
}
Пример с Custom HandlerMapping
@Component
public class CustomHandlerMapping implements HandlerMapping {
private static final Logger logger = LoggerFactory.getLogger(
CustomHandlerMapping.class);
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception {
String requestURI = request.getRequestURI();
String method = request.getMethod();
logger.info("Mapping request: {} {}", method, requestURI);
// Логика маппинга
if (requestURI.startsWith("/api/")) {
ApiController handler = new ApiController();
return new HandlerExecutionChain(handler,
new LoggingInterceptor(),
new SecurityInterceptor()
);
} else if (requestURI.startsWith("/web/")) {
WebController handler = new WebController();
return new HandlerExecutionChain(handler,
new LoggingInterceptor()
);
}
// Если маппинг не найден
return null;
}
@Override
public int getOrder() {
// Порядок выполнения (ниже = выше приоритет)
return 0;
}
}
Порядок поиска HandlerMapping
Spring ищет подходящий HandlerMapping в определённом порядке (по order):
@Configuration
public class MappingOrderConfig {
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping =
new RequestMappingHandlerMapping();
mapping.setOrder(0); // Проверяется первым
return mapping;
}
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(1); // Проверяется вторым
return mapping;
}
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
BeanNameUrlHandlerMapping mapping =
new BeanNameUrlHandlerMapping();
mapping.setOrder(2); // Проверяется третьим
return mapping;
}
}
Полный процесс DispatcherServlet
// Упрощённая версия работы DispatcherServlet
public class DispatcherServlet extends HttpServlet {
private List<HandlerMapping> handlerMappings;
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
try {
// 1. Получить HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// Ошибка 404
response.sendError(404);
return;
}
// 2. Выполнить pre-handle перехватчики
for (HandlerInterceptor interceptor :
mappedHandler.getInterceptors()) {
if (!interceptor.preHandle(processedRequest,
response, mappedHandler.getHandler())) {
return; // Отклонили запрос
}
}
// 3. Выполнить основной обработчик
Object handler = mappedHandler.getHandler();
ModelAndView mv = handleRequest(handler, processedRequest,
response);
// 4. Выполнить post-handle перехватчики
for (HandlerInterceptor interceptor :
mappedHandler.getInterceptors()) {
interceptor.postHandle(processedRequest, response,
handler, mv);
}
// 5. Отправить ответ
response.getWriter().write(mv.toString());
} finally {
// 6. Выполнить after-completion перехватчики
if (mappedHandler != null) {
for (HandlerInterceptor interceptor :
mappedHandler.getInterceptors()) {
interceptor.afterCompletion(processedRequest,
response, mappedHandler.getHandler(), null);
}
}
}
}
protected HandlerExecutionChain getHandler(
HttpServletRequest request) throws Exception {
// Пройти по всем HandlerMappings в порядке приоритета
for (HandlerMapping mapping : handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler; // Найден!
}
}
return null; // Не найден
}
}
Практический пример с логированием
@Component
public class LoggingHandlerMapping implements HandlerMapping,
Ordered {
private final RequestMappingHandlerMapping delegate;
private static final Logger logger = LoggerFactory.getLogger(
LoggingHandlerMapping.class);
public LoggingHandlerMapping(
RequestMappingHandlerMapping delegate) {
this.delegate = delegate;
}
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception {
HandlerExecutionChain chain = delegate.getHandler(request);
if (chain != null) {
Object handler = chain.getHandler();
logger.info("Mapped {} {} to {}",
request.getMethod(),
request.getRequestURI(),
handler.getClass().getSimpleName()
);
} else {
logger.warn("No mapping found for {} {}",
request.getMethod(),
request.getRequestURI()
);
}
return chain;
}
@Override
public int getOrder() {
return delegate.getOrder();
}
}
Лучшие практики
-
Используй @RequestMapping с явным методом HTTP
@RequestMapping(value = "/users", method = RequestMethod.GET) // или проще: @GetMapping("/users") -
Понимай порядок HandlerMappings
- RequestMappingHandlerMapping выполняется первым
- Более специфичные маппинги должны иметь ниже
order
-
Используй перехватчики через WebMvcConfigurer
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/public/**"); } } -
Избегай создания собственных HandlerMappings — обычно стандартные достаточно
Заключение
HandlerMapping — это фундаментальный компонент Spring, который связывает HTTP запросы с обработчиками. В большинстве случаев ты работаешь с ним через @RequestMapping и @RestController, не задумываясь о деталях, но понимание того, как это работает, помогает при отладке и настройке сложных сценариев маршрутизации.