상세 컨텐츠

본문 제목

[Spring] Argument Resolver 개념 / Custom Argument Resolver 생성

Spring/개념

by Chan.94 2024. 6. 17. 21:56

본문

반응형

Argument Resolver란?


흔히 Argument Resolver는 HandlerMethodArgumentResolver를 줄여서 부르는 용어다.
HandlerMethodArgumentResolver는 스프링 3.1에서 추가된 Interface이다.


Spring 공식 문서에는 다음과 같이 설명되어 있다.

Strategy interface for resolving method parameters into argument values in the context of a given request.
주어진 요청을 처리할 때, 메서드 파라미터를 인자값들에 주입해 주는 전략 Interface

 

즉, Argument Resolver를 사용하면 컨트롤러 메서드의 파라미터 중 특정 조건에 맞는 파라미터가 있다면, 요청에 들어온 값을 이용해 원하는 객체를 만들어 바인딩해 줄 수 있다.

 

Argument Resolver 실행시점


 



  1. HandlerMapping을 통해 적절한 Handler(Controller)를 찾고 파라미터에 대한 정보를 HandlerAdaper에게 전달한다.
  2. Handler(Controller)에 맞는 HandlerAdapter를 찾고 HandlerAdapter는 Hanlder(Controller)로 넘겨줄 파라미터를 결정하기 위해 이 작업을 HandlerMethodArgumentResolver에게 위임한다.
  3. HandlerMethodArgumentResolver는 Handler(Controller)로 전달할 파라미터를 결정한 후 데이터를 다시 HandlerAdapter에게 전달한다.
    HandlerAdapter는 HandlerMethodArgumentResolver로부터 전달받은 데이터를 파라미터로 포함시킨 후, Handler(Controller)를 호출한다.

스프링은 기본적으로 30개가 넘는 ArgumnetResolver를 제공한다.

 

Custom Argumenr Resolver 생성


Argument Resolver를 만들기 위해서는 HandlerMethodArgumentResolver를 상속받는 객체를 구현해야 한다. HandlerMethodArgumentResolver는 두 개의 메서드를 가지고 있다

 

supportsParameter()

주어진 메서드의 파라미터가 이 Argument Resolver에서 지원하는 타입인지 검사한다. 그리고 일치 여부를 boolean 타입으로 반환한다.

보통 CustomAnnotation을 생성하여 파라미터에 붙이거나 특정 객체를 상속받았는지를 확인하는 방법을 사용한다.

 

resolveArgument()

supportsParameter메서드에서 true를 반환하였을 경우 호출된다. 이 메서드의 반환값이 대상이 되는 메서드의 파라미터에 바인딩된다.

 


Step1. CustomAnnotation 생성

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomAnnotation {

}

 

  • @Retention 은 어노테이션이 어디까지 메모리를 유지할 지에 대한 설정이다. 간단하게 어노테이션의 생명주기를 설정해 준다고 생각하면 된다.
    RetentionPolicy.RUNTIME은 실행될 때까지 어노테이션을 유효하게 하겠다는 정책 설정
  • @Target은 정의한 어노테이션이 붙을 수 있는 타입을 정의하는 부분이다.
    PARAMETER는 파라미터에 붙을 수 있는 어노테이션이라는 의미

 

Step2. Custom Argumenr Resolver 생성 (HandlerMethodArgumentResolver를 상속받는 객체 구현)

@Component
public class CustomArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // @CustomAnnotation 어노테이션을 가지고 있는지
        boolean bHasLoginAnnotation = parameter.hasParameterAnnotation(CustomAnnotation.class);
        // 파라미터의 타입이 DevLog 혹은 DevLog의 자식클래스인지
        boolean bDevLogType = DevLog.class.isAssignableFrom(parameter.getParameterType());
        return bHasLoginAnnotation && bDevLogType;
    }
    
    @Override
    public Object resolveArgument(
            MethodParameter parameter,
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception {
        DevLog devLog;
        ...
        return devLog;
    }
}

resolveArgument의 파라미터 정보

  • MethodParameter
    요청 Handler 메서드가 반환하는 값을 저장하는 컨테이너이고 파라미터 타입, 어노테이션 등의 정보를 가지고 있다.
  • ModelAndViewContainer
    요청 Handler 메서드가 반환하는 값을 저장하는 컨테이너
  • NativeWebRequest
    현재 HTTP 요청에 대한 정보를 제공하는 인터페이스
  • WebDataBinderFactory
    요청 Handler 메서드의 Parameter를 Binding 할 데이터 바인더를 생성하는 팩토리

resolveArgument 구현부에 원하는 로직을 구현하면 된다.

Handler에서 반복적으로 수행하던 부분을 구현하여 소스를 간결하게 할 수도 있다.

 

Step3. Argument Resolver 등록

WebMvcConfigurer를 구현한 Configuration 클래스에 생성한 Argument Resolver를 등록한다.

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {

    private final CustomArgumentResolver customArgumentResolver;
    
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(customArgumentResolver);
    }
}

 

 

Step4. Handler(Controller) 구현

@PostMapping("/{urlId}")
public ResponseEntity<Void> createItem(@PathVariable("urlId") String data, @CustomAnnotation DevLog devLog) {
    return ResponseEntity.ok().build();
}

 

DevLog 객체이면서 @CustomAnnotation이 붙어있기 때문에 해당 메서드가 실행되기 전에 CustomArgumentResolver가 실행되어 DevLog에 원하는 header정보를 넣거나 Custom 할 수 있다.

 


RequestMappingHandlerAdapter.java

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ModelAndView mav;
        ...
        mav = invokeHandlerMethod(request, response, handlerMethod);
        ...
        return mav;
    }


    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        ...
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        ...

        // Handler 호출
        invocableMethod.invokeAndHandle(webRequest, mavContainer);

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
}

RequestMappingHandlerAdapter에서 Handler(Controller)가 실행되는 주요 소스이다.

ModelAndViewContainer에 Handler(Controller) 실행 결과가 담기게 된다.

 

ServletInvocableHandlerMethod.java

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

        try {
            this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        }
        catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }
}

mavContainer에 Handler(Controller) 실행 결과가 들어있다.

 

Handler(Controller) 실행 이후 반환값으로 Object 타입의 returnValue가 반환되고 Return Value Handler가 실행되는 것을 확인할 수 있다.

 

Return Value Hanlder에 대한 내용은 다음 포스팅에 이어서 하도록 하겠다.

[Spring/개념] - [Spring] Return Value Handler 개념 / Custom Return Value Handler 생성

반응형

관련글 더보기

댓글 영역

>