Spring MVC - ArgumentResolver

목차

참고

Spring Boot ArgumentResolver

ArgumentResolver 를 통해 Spring 에서는 어노테이션 기반 Controller에 다양한 파라미터를 사용할 수 있다.

스프링에서 컨트롤러를 생성할 때 메소드 매게변수로 다양한 파라미터들을 넣을 수 있다. 메소드에 다양한 파라미터를 넣을 수 있도록 도움을 주는 게 바로 ArgumentResolver 다.

HandlerMethodArgumentResolver.java

  • HandlerMethodArgumentResolver 인터페이스를 구현함으로서 Controller 에서 넘어오는 Parameter 를 초기화 할 수 있다.
  • supportsParameter 메소드를 통해 전달 받은 파라미터를 지원하는지 확인한 후 해당 파라미터를 지원하면 resolveArgument 를 통해 전달 받은 파리미터에 해당하는 객체를 생성한 후 Controller에 넘겨준다.
public interface HandlerMethodArgumentResolver {

boolean supportsParameter(MethodParameter parameter);

@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

HttpEntityMethodProcessor.java

HttpEntity, RequestEntity 객체를 초기화 하는 ArgumentResolver

  • Parameter 의 타입이 HttpEntity, RequestEntity 인지 확인한다.
public boolean supportsParameter(MethodParameter parameter) {
return HttpEntity.class == parameter.getParameterType() || RequestEntity.class == parameter.getParameterType();
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {

ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
Type paramType = getHttpEntityType(parameter);

if (paramType == null) {
throw new IllegalArgumentException("HttpEntity parameter '" + parameter.getParameterName() +
"' in method " + parameter.getMethod() + " is not parameterized");
}

Object body = readWithMessageConverters(webRequest, parameter, paramType);
if (RequestEntity.class == parameter.getParameterType()) {
// RequestEntity 객체를 반환한다.
return new RequestEntity<>(body, inputMessage.getHeaders(),
inputMessage.getMethod(), inputMessage.getURI());
}
else {
// HttpEntity 객체를 반환한다.
return new HttpEntity<>(body, inputMessage.getHeaders());
}
}

AbstractMessageConverterMethodArgumentResolver.java

@SuppressWarnings("unchecked")
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}

Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}

HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;

EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}

if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType,
getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
}

MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});

return body;
}
Share