목차
Spring MVC Request Life Cycle - HandlerMapping
RequestMapping 어노테이션을 처리하는 Handler, Handler Adapter는 RequestMappingHandlerMapping, RequestMappingHandlerAdapter다
HandlerMapping 살펴보기
Request(요청)와 Request를 처리하기 위한 Handler의 Mapping 정보를 관리한다.
HttpServletRequest 객체 Attribute에서 Handler의 정보를 가져올 수 있다.
BEST_MATCHING_HANDLER_ATTRIBUTE
요청 URL과 가장 많이 일치하는 Handler를 가져온다.
PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE
요청을 처리하기 위한 Handler의 URL을 가져온다.
BEST_MATCHING_PATTERN_ATTRIBUTE
요청 URL과 가장 많이 일치하는 Handler Pattern(URL)을 가져온다.
INTROSPECT_TYPE_LEVEL_MAPPING
URI_TEMPLATE_VARIABLES_ATTRIBUTE
요청 URL에 포함된 PathVariable 정보를 가져온다.
MATRIX_VARIABLES_ATTRIBUTE
요청 URL에 포함된 MATRIX 변수 정보를 가져온다.
Matrix 변수를 사용하기 위해서는 WebMvcConfigurer 를 이용해 설정해줘야 한다.
반환 값은 MultiValueMap 형태의 데이터다.
PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
HttpServletRequest 객체내 Attribute에서 Handler가 반환하는 Media Type 정보를 가져온다.
Matrix 변수 사용하기 - https://www.baeldung.com/spring-mvc-matrix-variables
HandlerMapping.java
public interface HandlerMapping { String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler" ; @Deprecated String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath" ; String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping" ; String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern" ; String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping" ; String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables" ; String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables" ; String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes" ; default boolean usesPathPatterns () { return false ; } @Nullable HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception; }
요청을 처리할 수 있는 Handler 가져오기
AbstractHandlerMapping 는 HandlerMapping 을 구현한 추상 클래스, 요청 경로와 Mapping된 Hanlder Interceptor 와 Default Hanlder 를 지원한다.
Request를 처리하기 위한 Hander를 가져온다.
AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null ) { handler = getDefaultHandler(); } if (handler == null ) { return null ; } if (handler instanceof String) { String handlerName = (String) handler; handler = obtainApplicationContext().getBean(handlerName); } if (!ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); if (logger.isTraceEnabled()) { logger.trace("Mapped to " + handler); } else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) { logger.debug("Mapped to " + executionChain.getHandler()); } if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { CorsConfiguration config = getCorsConfiguration(handler, request); if (getCorsConfigurationSource() != null ) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig != null ? globalConfig.combine(config) : config); } if (config != null ) { config.validateAllowCredentials(); } executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; }
AbstractHandlerMapping.java
protected String initLookupPath (HttpServletRequest request) { if (usesPathPatterns()) { request.removeAttribute(UrlPathHelper.PATH_ATTRIBUTE); RequestPath requestPath = ServletRequestPathUtils.getParsedRequestPath(request); String lookupPath = requestPath.pathWithinApplication().value(); return UrlPathHelper.defaultInstance.removeSemicolonContent(lookupPath); } else { return getUrlPathHelper().resolveAndCacheLookupPath(request); } }
AbstractHandlerMapping.java
protected HandlerExecutionChain getHandlerExecutionChain (Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain (handler)); for (HandlerInterceptor interceptor : this .adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
AbstractHandlerMethodMapping.java
protected HandlerMethod getHandlerInternal (HttpServletRequest request) throws Exception { String lookupPath = initLookupPath(request); this .mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null ); } finally { this .mappingRegistry.releaseReadLock(); } }
AbstractHandlerMethodMapping.java
@Nullable protected HandlerMethod lookupHandlerMethod (String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList <>(); List<T> directPathMatches = this .mappingRegistry.getMappingsByDirectPath(lookupPath); if (directPathMatches != null ) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { addMatchingMappings(this .mappingRegistry.getRegistrations().keySet(), matches, request); } if (!matches.isEmpty()) { Match bestMatch = matches.get(0 ); if (matches.size() > 1 ) { Comparator<Match> comparator = new MatchComparator (getMappingComparator(request)); matches.sort(comparator); bestMatch = matches.get(0 ); if (logger.isTraceEnabled()) { logger.trace(matches.size() + " matching mappings: " + matches); } if (CorsUtils.isPreFlightRequest(request)) { for (Match match : matches) { if (match.hasCorsConfig()) { return PREFLIGHT_AMBIGUOUS_MATCH; } } } else { Match secondBestMatch = matches.get(1 ); if (comparator.compare(bestMatch, secondBestMatch) == 0 ) { Method m1 = bestMatch.getHandlerMethod().getMethod(); Method m2 = secondBestMatch.getHandlerMethod().getMethod(); String uri = request.getRequestURI(); throw new IllegalStateException ( "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}" ); } } } request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod()); handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.getHandlerMethod(); } else { return handleNoMatch(this .mappingRegistry.getRegistrations().keySet(), lookupPath, request); } }