HandlerMethodArgumentResolverComposite Class Composite 패턴

2020. 9. 3. 18:48 Spring Framework/Spring Core

오늘 부터 시간이 나는대로 스프링 프레임워크에서 처리해주는 기능들이 어떻게 동작하는지 소스 리뷰를 하기 시작했다.

@RequestBody와 @ResponseBody가 어떻게 동작하는지 궁금해서 spring-web 프로젝트를 조금 살펴보다가 HandlerMethodArgumentResolverComposite 클래스라는 놈을 만나게되었는데, Composite라는 패턴을 사용했다. 사실 Composite 패턴이라는걸 예전에 책을 통해 한번 본거 같은 기억이 나지만 잘 와닿지 않았는데 스프링 소스를 보니 이해가 잘된다.


  먼저 Composite 패턴에 대해서 설명은 생략하겠다. 참고


Diagram



  HandlerMethodArgumentResolver는 Request 요청에 대한 파라미터 데이터에 대해 컨트롤 할 수 있는 Interface다. 

실제로 구현체인 HandlermethodArgumentResolverComposite와 RequestRespoinseBodyMethodProcessor가 있는데, RequestRespoinseBodyMethodProcessor가 내가 찾던 @RequestBody와 @ResponseBody를 동작 시키는 클래스이고 Composite 패턴이 적용된 HandlermethodArgumentResolverComposite 클래스가 존재한다. 물론 더 많은 구현체가 있지만 생략한다.

제일 밑에 RequestMappingHandlerAdapter 클래스는 HandlerMethodArgumentResolver를 실행 시키는 클래스(사실 실제 실행은 아니고 객체 조합을 담당한다.)로 Controller가 호출되기 위해서는 이 RequestMappingHandlerAdapter라는 클래스가 필요하다.(참고)


HandlermethodArgumentResolverComposite

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
29
30
31
32
33
34
private final List<Handlermethodargumentresolver> argumentResolvers = new LinkedList<Handlermethodargumentresolver>();
private final Map<Methodparameter, Handlermethodargumentresolver> argumentResolverCache = new ConcurrentHashMap<Methodparameter, Handlermethodargumentresolver>(256);
 
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
 
    HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
    }
    return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
 
/**
 * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
 */
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
                        parameter.getGenericParameterType() + "]");
            }
            if (methodArgumentResolver.supportsParameter(parameter)) {
                result = methodArgumentResolver;
                this.argumentResolverCache.put(parameter, result);
                break;
            }
        }
    }
    return result;
}
cs

  참고 문서에 나와있는 것과 약간 다른점이 있다면 HandlerMethodArgumentResolverComposite 클래스는 List 배열에 담고 있다가 cache 처리를 하기 위해 map에 key를 MethodParameter 객체로 만든다.


  디자인 패턴 공부를 어떻게 해야될까 고민을 많이 했을 시기가 있었는데, 그냥 책을 보자니 잘 와 닿지 않았고 예제 코드를 봐도 실무 코드에 적용하기는 많은 연습이 필요하다고 생각했다. 솔직히 고민만하고 그냥 넘어간듯한데, 이렇게 실제 프레임워크 코드를 보면서 패턴을 보니 좀 더 와 닿는다. 너무 급하지 않게 꾸준히 코드 리뷰를 해야겠다.

그나저나 코드 보는 것도 중요한데 레퍼런스 문서하고 같이 보는 습관을 길러야겠다. 문제는 영어!!

출처 : https://blog.woniper.net/306?category=699184