흔히 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를 사용하면 컨트롤러 메서드의 파라미터 중 특정 조건에 맞는 파라미터가 있다면, 요청에 들어온 값을 이용해 원하는 객체를 만들어 바인딩해 줄 수 있다.
스프링은 기본적으로 30개가 넘는 ArgumnetResolver를 제공한다.
Argument Resolver를 만들기 위해서는 HandlerMethodArgumentResolver를 상속받는 객체를 구현해야 한다. HandlerMethodArgumentResolver는 두 개의 메서드를 가지고 있다
supportsParameter()
주어진 메서드의 파라미터가 이 Argument Resolver에서 지원하는 타입인지 검사한다. 그리고 일치 여부를 boolean 타입으로 반환한다.
보통 CustomAnnotation을 생성하여 파라미터에 붙이거나 특정 객체를 상속받았는지를 확인하는 방법을 사용한다.
supportsParameter 메서드는 한 번 호출된 후 내부적으로 캐싱된다.
이를 통해 성능을 최적화하며, 동일한 MethodParameter에 대해 중복 호출을 방지한다.
resolveArgument()
supportsParameter메서드에서 true를 반환하였을 경우 호출된다. 이 메서드의 반환값이 대상이 되는 메서드의 파라미터에 바인딩된다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface CustomAnnotation {
}
@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의 파라미터 정보
resolveArgument 구현부에 원하는 로직을 구현하면 된다.
Handler에서 반복적으로 수행하던 부분을 구현하여 소스를 간결하게 할 수도 있다.
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);
}
}
@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] Return Value Handler 개념 / Custom Return Value Handler 생성
DispatcherServlet에서 ArgumentResolver가 어떤 시점에 호출되는지 소스레벨로 보고싶다면 아래 포스팅을 확인하기 바란다.
Dispatcher Servlet 파헤치기 (Argument Resolver, ReturnValue Handler, Controller 실행시점)
[Spring] Dispatcher Servlet 파헤치기 (Argument Resolver, ReturnValue Handler, Controller 실행시점) (39) | 2024.06.30 |
---|---|
[Spring] Return Value Handler 개념 / Custom Return Value Handler 생성 (1) | 2024.06.17 |
@Transactional 기초 및 주의사항 (8) | 2023.05.06 |
크롤링 - 셀레니움(Selenium)/ChromeOptions/WebDriverWait (1) | 2022.09.28 |
메세지 다국어 처리 / 공통 Message 처리 (MessageSource) (0) | 2022.09.19 |
댓글 영역