티스토리 뷰
입력된 데이터에 가장 부합하는 조건 찾는 코드.
Collection.sort() 와 Compartor.java 를 활용한 것이 인상적이였으며 Spring 에서 HTTP Request 를 Handling 할 RequestMapping 을 찾을때 사용되는 코드를 눈으로 확인하여서 흥미로웠다.
RequestMapping 에는 여러가지 조건이 들어갈 수 있다.
@RequestMapping(value = { "/main" }, method = { RequestMethod.GET, RequestMethod.POST }, headers="X-PROXY-DEVICE=MOBILE") @RequestMapping(value = { "/main" }, method = { RequestMethod.GET, RequestMethod.POST }, headers="X-PROXY-DEVICE=PC")
위 코드 처럼 URL, HTTP Method, Header 등의 조건들을 적용할 수 있다.
내가 개발중인 서버에서 Mobile Web 을 지원하기 시작하면서 해당 서버의 앞단에 있는 Proxy 서버에서 X-PROXY-DEVICE 라는 헤더 값을 내려주기 시작하였다.
따라서 X-PROXY-DEVICE=MOBILE 인 요청은 Mobile web view 를 리턴하고 X-PROXY-DEVICE=PC 인 경우에는 PC web view 를 리턴하도록 코드를 작성하였다.
하지만 한가지 문제가 발생하였는데 local 에서 테스트를 할때는 Proxy 를 거치지 않아 위와 같이 /main 요청에 대해서 Header 조건을 설정해 놓으면 프록시도 로컬에서 띄워서 테스트하지 않는 이상 매칭되는 RequesMapping 이 없으므로 404 가 리턴된다.
크롬 확장프로그램을 사용하여 Header 를 전달하는 방법도 있지만 모든 개발자들이 확장 프로그램을 설치하고 로컬 테스트를 진행하는 것은 낭비라고 생각하여 다른 방법을 생각해보았다.
@RequestMapping(value = { "/main" }, method = { RequestMethod.GET, RequestMethod.POST }, headers="X-PROXY-DEVICE=MOBILE") @RequestMapping(value = { "/main" }, method = { RequestMethod.GET, RequestMethod.POST })
조건을 위와 같이 변경하여 헤더가 없는경우, PC 헤더인 경우는 디폴트로 PC view 를 리턴하는 RequestMapping 으로 요청이 전달되도록 수정해보았다.
1. header 가 없는 경우 -> default 인 PC 로
2. pc header 가 있는 경우 -> default 인 PC 로
3. mobile header 가 있는 경우 -> mobile 로
요청이 들어갔다.
더 구체적으로 매칭되는 RequestMapping 에 요청이 전달되는 것을 로컬 테스트로 확인하였지만 이를 보장하기 위해 확신을 가지고 싶었다.
Spring 에서 HandlerMethod 를 찾기 위한 AbstractHandlerMethodMapping.java 의 lookupHandlerMethod() 코드이다.
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.urlMap.get(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... addMatchingMappings(this.handlerMethods.keySet(), matches, request); } if (!matches.isEmpty()) { Comparator<Match> comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } Match bestMatch = matches.get(0); if (matches.size() > 1) { Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException( "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(handlerMethods.keySet(), lookupPath, request); } }
우선 URL 이 일치하는 Match.java 를 찾는다. directpathMatches 에 저장
directpathMatches 중 현재 조건에 부합하는 Match 를 addMatchingMappings() 를 통해서 matches 에 저장한다.
즉 matches 에는 현재 조건에 푸합하는 Match 들만 저장되어있는 상태
matches 을 MatchComparator.java 를 이용해서 Sorting 을 한 후에 첫 번째 인자를 사용하도록 리턴한다.
MatchComparator 에서 호출하는 compareTo 메서드는 아래와 같다.
public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); if (result != 0) { return result; } result = this.paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } result = this.headersCondition.compareTo(other.getHeadersCondition(), request); if (result != 0) { return result; } result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); if (result != 0) { return result; } result = this.producesCondition.compareTo(other.getProducesCondition(), request); if (result != 0) { return result; } result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } result = this.customConditionHolder.compareTo(other.customConditionHolder, request); if (result != 0) { return result; } return 0; }
위와 같은 조건으로 sorting 을 진행하고 있기 때문에 더 구체적인 Match 가 먼저 사용된다.
다만 compareTo 함수의 조건이 걸린 순서대로 구체적인 Match 가 사용된다.
예를 들어서 Pattern 이 더 구체적인 Match 가 있고 Headers 가 더 구체적인 Match 가 있다면
compareTo 조건문들의 순서에 의해서 Pattern 이 더 구체적인 Match 가 사용된다.
'Spring Framework' 카테고리의 다른 글
Spring JPA 와 Dynamic Proxy (0) | 2022.04.24 |
---|---|
Spring Bean 과 CGLIB proxy (0) | 2022.04.23 |
Spring Framework - RedirectAttributes 설명 (0) | 2021.12.17 |
- Total
- Today
- Yesterday
- circurit breaker
- getBoolean
- Lazy
- wait()
- ResultSet
- ConcurrentHashMap
- dynamodb
- custom config data convertion
- N+1
- aurora
- Flux
- MariaDB
- mariada-connector
- notify()
- RoutePredication
- mariadb-connector-j
- AbstractMethodError
- reative
- msyql-connector-java
- rate limit
- DyanomoDB
- notifyAll()
- HashMap
- spring cloud gateway
- RouteDefinition
- router
- Seperate Chaining
- GlobalFilter
- reactor
- referencedColumnName
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |