지난 포스팅에서는 어댑터(Adapter) 패턴이 적용된 SpringMVC 패턴을 다루어보았다. 이번 포스팅에서는 실제 SpringMVC의 어댑터 패턴의 구조를 보며, HandlerAdapter에 대해서 다루어 보겠다.
Front-Servlet이 Controller의 비즈니스 로직을 직접 실행한다면 한 가지 유형의 Controller에 종속된다. 그러므로 Adapter 패턴을 적용하여 다양한 유형의 Controller를 지원할 수 있도록 해야한다. SpringWeb이 지원하는 Front-Servlet은 DispatcherServlet이다.
디버깅으로 DispatcherServlet의 handlerAdapter 구현체 리스트에 담겨있는 객체들을 확인해보았다. 그럼 이를 그림으로 표현해보자.
요청(Request)이 들어오면 요청의 url과 매핑되는 handler(Controller 구현체)가 생성된다. handler의 비즈니스 로직을 DispatcherServlet이 직접 실행하면 DispatcherServlet은 한 가지 유형의 handler에 종속되어 버린다. 그래서 handler 실행권한을 HandlerAdapter에게 넘기어야 한다. 그래야 다양한 유형의 handler가 실행 가능한 구조가 만들어 진다.
DispatcherServlet이 지원하는 HandlerAdapter 구현체는 4가지가 있다.
1) RequestMappingHandlerAdapter
2) HandlerFunctionAdapter
3) HttpRequestHandlerAdapter
4) SimpleControllerHandlerAdapter
@Controller, @RequestMapping으로 선언된 handler ( RequestMappingHandlerAdapter )
@Controller, @RequestMapping으로 선언된 handler(Controller)는 RequestMappingHandlerAdapter가 지원한다. 가장 자주 사용되는 유형이라 할 수 있다.
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
//중략...
@Override
protected boolean isHandler(Class<?> beanType) { // Handler가 아래 유형인지 확인
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || //Controller 어노테이션
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); //RequestMapping 어노테이션
}
//중략...
}
실제로 RequestMappingHandler인지 확인하기 위해, Controller 어노테이션이나 RequestMapping 어노테이션으로 선언되어 있는지 확인하는 과정을 거친다.
이처럼 요청에 의해 생성된 Handler(Controller)가 @Controller나 @RequestMapping으로 선언되어 있다면 RequestMappingHandlerAdapter가 지원하는 Handler이다. Handler는 RequestMappingHandlerAdapter에 의해 비즈니스 로직이 실행된다.
이 외에도 다양한 유형의 Handler가 존재한다.
HandlerFunction 인터페이스 ( HandlerFunctionAdapter )
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
T handle(ServerRequest request) throws Exception;
}
HttpRequestHandler 인터페이스 ( HttpRequestHandlerAdapter )
@FunctionalInterface
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException;
}
Controller 인터페이스 ( SimpleControllerHandlerAdapter )
@FunctionalInterface
public interface Controller {
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Handler는 모두 함수형 인터페이스로 비즈니스로직을 처리하는 메소드만을 가지고 있다. 각 Handler를 담당하는 HandlerAdapter는 Handler 구현체의 함수형 메소드를 실행하여 비즈니스 로직을 처리한다. 최근에는 어노테이션 방식을 주로 사용하기에, 이런 방식은 과거의 방식으로 남아있다. 그래도 SpringMVC는 어댑터 패턴으로 구현되어 있기에, 해당 유형의 Handler를 Request할 수 있다.
그럼 한번 실제로 만들어 보자.
Controller 인터페이스를 구현한 Handler 객체
@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 new ModelAndView("new-form");
}
}
HttpRequestHandler 인터페이스를 구현한 Handler 객체
@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MyHttpRequestHandler.handleRequest");
}
}
두 가지 유형은 모두 @Component에 bean 이름에 URL을 넣는다. Spring WEB은 bean의 이름을 Handler의 url로 인식한다. 그럼 테스트해보자.
url : http://localhost:8080/springmvc/old-controller
url : http://localhost:8080/springmvc/request-handler
테스트가 잘 이루어졌음을 확인할 수 있다.
이렇듯, SpringMVC 구조에 어댑터 패턴를 적용하여, DispatcherServlet이 하나의 Controller에 종속하지 않고 다양한 Controller를 지원할 수 있게 되었고 SpringMVC 구조는 유연하게 비즈니스 로직을 처리할 수 있게 되었다.
참고자료
'SPRING > Spring MVC' 카테고리의 다른 글
[SpringMVC] @RequestMapping의 다양한 속성 (0) | 2023.08.10 |
---|---|
[SpringMVC] @RequestMapping, @Controller (0) | 2023.08.10 |
[SpringMVC] MVC 패턴 구현하기(4) - Adapter (0) | 2023.08.09 |
[SpringMVC] MVC 패턴 구현하기(3) - Model (0) | 2023.08.09 |
[SpringMVC] MVC 패턴 구현하기(2) - Controller (0) | 2023.08.08 |