인터셉터를 이용해 Controller 공통 로직 처리하기에서 이어짐
웹 어플리케이션에 아규먼트 리졸버를 적용해 HTTP요청 헤더 정보를 저장하고 있는 HeaderInfo 인자타입이 메소드에 있을 경우 자동으로 넘겨주는 예제를 작성해 보자
package kr.or.connect.guestbook.argumentresolver;
import java.util.HashMap;
import java.util.Map;
public class HeaderInfo {
private Map<String, String> map;
public HeaderInfo() {
map = new HashMap<>();
}
public void put(String name, String value) {
map.put(name, value);
}
public String get(String name) {
return map.get(name);
}
}
package kr.or.connect.guestbook.argumentresolver;
import java.util.Iterator;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
public class HeaderMapArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HeaderInfo headerInfo = new HeaderInfo();
Iterator<String> headerNames = webRequest.getHeaderNames();
while(headerNames.hasNext()) {
String headerName = headerNames.next();
String headerValue = webRequest.getHeader(headerName);
System.out.println(headerName + " , " + headerValue);
headerInfo.put(headerName, headerValue);
}
return headerInfo;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType() == HeaderInfo.class;
}
}
Controller에서 메서드의 인자가 4개일 경우에 supportsParameter 메서드가 4번 호출된다. supportsParameter는 인자의 정보를 parameter로 전달하게 되는데, 해당 파라미터 정보에 원하는 정보가 있다면 true를 반환하고 없으면 false를 반환한다.
Parameter 타입이 방금 만든 HeaderInfo.class 타입일 경우에 true를 리턴하도록 method를 오버라이드 하였다.
supportsParameter가 true를 리턴할 경우에만 resolveArgument 메서드가 호출된다. 이 resolveArgument가 리턴한 값은 Controller에서 메서드의 인자로 전달된다.
이를 구현하기 위해 코드에서는 요청으로 부터 넘어온 모든 Header 정보를 HeaderInfo 객체에 담아서 리턴하고 있다.
package kr.or.connect.guestbook.config;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import kr.or.connect.guestbook.argumentresolver.HeaderMapArgumentResolver;
import kr.or.connect.guestbook.interceptor.LogInterceptor;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "kr.or.connect.guestbook.controller" })
public class WebMvcContextConfiguration extends WebMvcConfigurerAdapter{
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// default servlet handler를 사용하게 합니다.
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
System.out.println("addViewControllers가 호출됩니다. ");
registry.addViewController("/").setViewName("index");
}
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new LogInterceptor());
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
System.out.println("아규먼트 리졸버 등록..");
argumentResolvers.add(new HeaderMapArgumentResolver());
}
}
생성한 Argument Resolver를 적용하기 위해서는 WebMvcContextConfiguration.java에서 addArgumentResolvers 메서드를 오버라이딩 하고 인자로 넘어온 argumentResolver에 앞에서 생성한 argumentResolver를 추가한다.(add)
package kr.or.connect.guestbook.controller;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import kr.or.connect.guestbook.argumentresolver.HeaderInfo;
import kr.or.connect.guestbook.dto.Guestbook;
import kr.or.connect.guestbook.service.GuestbookService;
@Controller
public class GuestbookController {
@Autowired
GuestbookService guestbookService;
@GetMapping(path="/list")
public String list(@RequestParam(name="start", required=false, defaultValue="0") int start,
ModelMap model,
@CookieValue(value="count", defaultValue="0", required=true) String value,
HttpServletResponse response,
HeaderInfo headerInfo
){
System.out.println("-----------------------------------------------------");
System.out.println(headerInfo.get("user-agent"));
System.out.println("-----------------------------------------------------");
...
Argument Resolver는 해당 인자가 넘어왔을 때 사용된다고 했었다. 기존에 사용하던 Controller에 인자를 넘기도록 HeaderInfo headerInfo를 파라미터에 추가하자. headerInfo의 get메서드에 user-agent를 넘기면 값이 출력되는 것을 알 수 있다.
컨트롤러 메소드에서 자주 사용되는 값이 있을 경우 아규먼트 리졸버를 만들어서 넘겨주도록 하면 편리하게 사용할 수 있다.