Spring
스프링의 핸들러 매핑과 어댑터
monkeyDugi
2022. 1. 24. 12:10
반응형
스프링은 어댑터 패턴 기반의 프론트 컨트롤러 패턴으로 컨트롤러를 제공하는
핸들러 매핑과 어댑터를 제공한다.
과거에 사용했던 것들을 알아 보자.
Controller 인터페이스
- @Controller 아님
package org.springframework.web.servlet.mvc;
@FunctionalInterface
public interface Controller {
/**
* Process the request and return a ModelAndView object which the DispatcherServlet
* will render. A {@code null} return value is not an error: it indicates that
* this object completed request processing itself and that there is therefore no
* ModelAndView to render.
* @param request current HTTP request
* @param response current HTTP response
* @return a ModelAndView to render, or {@code null} if handled directly
* @throws Exception in case of errors
*/
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
package org.springframework.web.servlet.handler;
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
package org.springframework.web.servlet.mvc;
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
@SuppressWarnings("deprecation")
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
만약 직접 HandlerAdapter를 만들고 싶다면 HandlerAdapter만 구현해주면 된다.
위의 핸들러 인터페이스를 구현 해보면 아래와 같다.
빈의 이름으로 url을 매핑하게 된다.
@Component("/springmvc/old-controller")
public class OldController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("OldController.handleRequest");
return null;
}
}
컨트롤러가 호출되는 과정
HandlerMapping
- 핸들러 매핑에서 이 컨트롤러를 찾을 수 있어야 한다.
- 예) 스프링 빈의 이름으로 핸들러(Controller)를 찾을 수 있는 핸들러 매핑이 필요하다.
HandlerAdapter
- 핸들러 매핑을 통해서 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다.
- 예)
Controller 인터페이스
를 실행할 수 있는 핸들러 어댑터를 찾고 실행한다.
스프링 부트가 자동으로 등록하는 핸들러 매핑과 핸들러 어댑터
스프링에는 거의 다 제공이 되기 때문에 2개만 알아보도록 하자.
handlerMapping
앞의 숫자는 우선 순위 이다.
결과적으로는 RequestMappingHandleMapping, RequestMappingHandlerAdapter
를 거의 사용한다.
0 = RequestMappingHandleMapping : 애노테이션 기반의 컨트롤러인 @RequestMapping에서 사용
1 = BeanNameUrlHandleMppaing : 스프링 빈 이름으로 핸들러를 찾는다.
HandlerAdapter
0 = RequestMappingHandlerAdapter : 애노테이션 기반의 컨트롤러인 @RequestMapping에서 사용
1 = HttpRequesthandlerAdapter : HttpRequestHandler 처리
2 = SimpleControllerHandleAdapter : Contoller 인터페이스(애노테이션 x, 과거에 사용) 처리
핸들러 매핑, 핸들러 어댑터를 모두 순서대로 찾고 만약 없으면 다음 순서로 넘어간다.
BeanNameUrlHandleMppaing, SimpleControllerHandleAdapter의 실행 과정을 알아보자.
- 핸들러 매핑으로 핸들러 조회
BeanNameUrlHandleMppaing
이OldContoller
를 반환한다.
- 핸들러 어댑터 조회
HandlerAdapter의 supports()
를 순서대로 호출한다.SimpleControllerHandleAdapter
가Controller 인터페이스
를 지원하므로 대상이 된다.
- 핸들러 어댑터 실행
- 디스패처 서블릿(프론트 컨트롤러)이 조회한
SimpleControllerHandleAdapter
를 실행하면서
핸들러 정보도 같이 넘겨준다. SimpleControllerHandleAdapter
는 핸들러인 OldController를 내부에서 실행하고, 그 결과를 반환한다.
- 디스패처 서블릿(프론트 컨트롤러)이 조회한
OldContoller 핸들러 매핑, 어댑터 정리
- HandlerMapping =
BenaNameUrlHandlerMapping
- HandlerAdapter =
SimpleControllerHandlerAdapter
@RueqestMapping
요즘은 위의 두 방식은 불편하기에 사용하지 않고, 이 방식을 사용한다.
아마 가장 익숙한 모양일 것이다.
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
각 애노테이션을 알아보자.
@Controller
- 내부에 @Component가 있기 때문에 스프링이 자동으로 빈으로 등록
@RequestMapping
- 요청 정보를 매핑한다.
- 해당 URL이 호출되면 이 메서드가 호출 된다. 애노테이션 기반이기 때문에 메서드 명은 상관없다.
ModelAndVeiw
- 모델과 뷰 정보를 담아서 반환한다.
RequestMappingHandleMapping
- 애노테이션 기반의 컨트롤러인
@RequestMapping
에서 사용 - 스프링 빈 중에서 @ReuqestMapping 또는 @Controller가 클래스 레벨에 부터 있으면
매핑 정보로 인식한다.
즉, 아래 코드도 동작한다.
@Component
@RequestMapping
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
선 조건이 빈으로 등록이 되어야 하는 것이기 때문에 아래 코드도
빈으로 등록만 해준다면 동작한다.
@RequestMapping
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process() {
return new ModelAndView("new-form");
}
}
단, 빈 등록을 수동으로 해주어야 함..
@ServletComponentScan // 서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
@Bean
SpringMemberFormControllerV1 springMemberFormControllerV1() {
return new SpringMemberFormControllerV1();
}
}
반응형