From cd71121474f660a8c40cb64d80bb1f480c25cd9c Mon Sep 17 00:00:00 2001 From: JFe <33208246+Go-Jaecheol@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:10:24 +0900 Subject: [PATCH] =?UTF-8?q?[MVC=20=EA=B5=AC=ED=98=84=ED=95=98=EA=B8=B0=20-?= =?UTF-8?q?=202=EB=8B=A8=EA=B3=84]=20=EB=A7=9D=EA=B3=A0(=EA=B3=A0=EC=9E=AC?= =?UTF-8?q?=EC=B2=A0)=20=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.=20(#531)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: HandlerMapping 인터페이스 구현 * feat: HandlerAdapter 인터페이스 및 ManualHandlerAdapter, AnnotationHandlerAdapter 구현 * feat: HandlerMappings, HandlerAdapters 구현 * feat: 인터페이스 기반, 어노테이션 기반 동시 지원하도록 DispatcherServlet 구현 * refactor: 어노테이션 방식으로 RegisterController 변경 * refactor: RegisterController URI 매핑 수정 * refactor: Override 어노테이션 추가 --- .../com/techcourse/DispatcherServlet.java | 28 +++++++++++------ .../java/com/techcourse/HandlerAdapters.java | 26 ++++++++++++++++ .../java/com/techcourse/HandlerMappings.java | 30 +++++++++++++++++++ .../com/techcourse/ManualHandlerMapping.java | 15 ++++++---- .../controller/RegisterController.java | 26 +++++++++++----- .../controller/RegisterViewController.java | 13 -------- .../org/springframework/web/servlet/View.java | 2 ++ .../mvc/asis/ManualHandlerAdapter.java | 23 ++++++++++++++ .../mvc/tobe/AnnotationHandlerAdapter.java | 20 +++++++++++++ .../mvc/tobe/AnnotationHandlerMapping.java | 4 ++- .../web/servlet/mvc/tobe/HandlerAdapter.java | 12 ++++++++ .../web/servlet/mvc/tobe/HandlerMapping.java | 10 +++++++ .../web/servlet/view/JsonView.java | 12 ++++++++ .../web/servlet/view/JspView.java | 8 +++++ 14 files changed, 193 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/com/techcourse/HandlerAdapters.java create mode 100644 app/src/main/java/com/techcourse/HandlerMappings.java delete mode 100644 app/src/main/java/com/techcourse/controller/RegisterViewController.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ManualHandlerAdapter.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerAdapter.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerAdapter.java create mode 100644 mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerMapping.java diff --git a/app/src/main/java/com/techcourse/DispatcherServlet.java b/app/src/main/java/com/techcourse/DispatcherServlet.java index 277d8eed9a..e2bfcb62bf 100644 --- a/app/src/main/java/com/techcourse/DispatcherServlet.java +++ b/app/src/main/java/com/techcourse/DispatcherServlet.java @@ -6,6 +6,10 @@ import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.asis.ManualHandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerAdapter; +import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping; import webmvc.org.springframework.web.servlet.view.JspView; public class DispatcherServlet extends HttpServlet { @@ -13,33 +17,39 @@ public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); - private ManualHandlerMapping manualHandlerMapping; + private final transient HandlerMappings handlerMappings; + private final transient HandlerAdapters handlerAdapters; public DispatcherServlet() { + this.handlerMappings = new HandlerMappings(); + this.handlerAdapters = new HandlerAdapters(); } @Override public void init() { - manualHandlerMapping = new ManualHandlerMapping(); - manualHandlerMapping.initialize(); + handlerMappings.addHandlerMapping(new ManualHandlerMapping()); + handlerMappings.addHandlerMapping(new AnnotationHandlerMapping()); + handlerAdapters.addHandlerAdapter(new ManualHandlerAdapter()); + handlerAdapters.addHandlerAdapter(new AnnotationHandlerAdapter()); } @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { - final String requestURI = request.getRequestURI(); - log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI); + log.debug("Method : {}, Request URI : {}", request.getMethod(), request.getRequestURI()); try { - final var controller = manualHandlerMapping.getHandler(requestURI); - final var viewName = controller.execute(request, response); - move(viewName, request, response); + final var handler = handlerMappings.getHandler(request); + final var handlerAdapter = handlerAdapters.getHandlerAdapter(handler); + final ModelAndView modelAndView = handlerAdapter.handle(request, response, handler); + move(modelAndView, request, response); } catch (Throwable e) { log.error("Exception : {}", e.getMessage(), e); throw new ServletException(e.getMessage()); } } - private void move(final String viewName, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + private void move(final ModelAndView modelAndView, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final String viewName = modelAndView.getView().getViewName(); if (viewName.startsWith(JspView.REDIRECT_PREFIX)) { response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length())); return; diff --git a/app/src/main/java/com/techcourse/HandlerAdapters.java b/app/src/main/java/com/techcourse/HandlerAdapters.java new file mode 100644 index 0000000000..a52409abba --- /dev/null +++ b/app/src/main/java/com/techcourse/HandlerAdapters.java @@ -0,0 +1,26 @@ +package com.techcourse; + +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapter; + +import java.util.ArrayList; +import java.util.List; + +public class HandlerAdapters { + + private final List adapters; + + public HandlerAdapters() { + this.adapters = new ArrayList<>(); + } + + public void addHandlerAdapter(final HandlerAdapter handlerAdapter) { + adapters.add(handlerAdapter); + } + + public HandlerAdapter getHandlerAdapter(final Object handler) { + return adapters.stream() + .filter(it -> it.supports(handler)) + .findFirst() + .orElseThrow(IllegalArgumentException::new); + } +} diff --git a/app/src/main/java/com/techcourse/HandlerMappings.java b/app/src/main/java/com/techcourse/HandlerMappings.java new file mode 100644 index 0000000000..53a681eea4 --- /dev/null +++ b/app/src/main/java/com/techcourse/HandlerMappings.java @@ -0,0 +1,30 @@ +package com.techcourse; + +import jakarta.servlet.http.HttpServletRequest; +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class HandlerMappings { + + private final List mappings; + + public HandlerMappings() { + this.mappings = new ArrayList<>(); + } + + public void addHandlerMapping(final HandlerMapping handlerMapping) { + handlerMapping.initialize(); + mappings.add(handlerMapping); + } + + public Object getHandler(final HttpServletRequest request) { + return mappings.stream() + .map(it -> it.getHandler(request)) + .filter(Objects::nonNull) + .findFirst() + .orElseThrow(IllegalStateException::new); + } +} diff --git a/app/src/main/java/com/techcourse/ManualHandlerMapping.java b/app/src/main/java/com/techcourse/ManualHandlerMapping.java index a54863caf8..194d4bc3cc 100644 --- a/app/src/main/java/com/techcourse/ManualHandlerMapping.java +++ b/app/src/main/java/com/techcourse/ManualHandlerMapping.java @@ -1,34 +1,39 @@ package com.techcourse; -import com.techcourse.controller.*; +import com.techcourse.controller.LoginController; +import com.techcourse.controller.LoginViewController; +import com.techcourse.controller.LogoutController; +import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import webmvc.org.springframework.web.servlet.mvc.asis.Controller; import webmvc.org.springframework.web.servlet.mvc.asis.ForwardController; +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping; import java.util.HashMap; import java.util.Map; -public class ManualHandlerMapping { +public class ManualHandlerMapping implements HandlerMapping { private static final Logger log = LoggerFactory.getLogger(ManualHandlerMapping.class); private static final Map controllers = new HashMap<>(); + @Override public void initialize() { controllers.put("/", new ForwardController("/index.jsp")); controllers.put("/login", new LoginController()); controllers.put("/login/view", new LoginViewController()); controllers.put("/logout", new LogoutController()); - controllers.put("/register/view", new RegisterViewController()); - controllers.put("/register", new RegisterController()); log.info("Initialized Handler Mapping!"); controllers.keySet() .forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass())); } - public Controller getHandler(final String requestURI) { + @Override + public Controller getHandler(final HttpServletRequest request) { + final String requestURI = request.getRequestURI(); log.debug("Request Mapping Uri : {}", requestURI); return controllers.get(requestURI); } diff --git a/app/src/main/java/com/techcourse/controller/RegisterController.java b/app/src/main/java/com/techcourse/controller/RegisterController.java index da62e5e8e9..389275f105 100644 --- a/app/src/main/java/com/techcourse/controller/RegisterController.java +++ b/app/src/main/java/com/techcourse/controller/RegisterController.java @@ -2,20 +2,30 @@ import com.techcourse.domain.User; import com.techcourse.repository.InMemoryUserRepository; +import context.org.springframework.stereotype.Controller; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; +import web.org.springframework.web.bind.annotation.RequestMapping; +import web.org.springframework.web.bind.annotation.RequestMethod; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.view.JspView; -public class RegisterController implements Controller { +@Controller +public class RegisterController { - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { + @RequestMapping(value = "/register", method = RequestMethod.POST) + public ModelAndView save(final HttpServletRequest request, final HttpServletResponse response) { final var user = new User(2, - req.getParameter("account"), - req.getParameter("password"), - req.getParameter("email")); + request.getParameter("account"), + request.getParameter("password"), + request.getParameter("email")); InMemoryUserRepository.save(user); - return "redirect:/index.jsp"; + return new ModelAndView(new JspView("redirect:/index.jsp")); + } + + @RequestMapping(value = "/register/view", method = RequestMethod.GET) + public ModelAndView show(final HttpServletRequest request, final HttpServletResponse response) { + return new ModelAndView(new JspView("/register.jsp")); } } diff --git a/app/src/main/java/com/techcourse/controller/RegisterViewController.java b/app/src/main/java/com/techcourse/controller/RegisterViewController.java deleted file mode 100644 index 136962136d..0000000000 --- a/app/src/main/java/com/techcourse/controller/RegisterViewController.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.techcourse.controller; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import webmvc.org.springframework.web.servlet.mvc.asis.Controller; - -public class RegisterViewController implements Controller { - - @Override - public String execute(final HttpServletRequest req, final HttpServletResponse res) throws Exception { - return "/register.jsp"; - } -} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java index 4499f36866..0346a3dda8 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/View.java @@ -7,4 +7,6 @@ public interface View { void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception; + + String getViewName(); } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ManualHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ManualHandlerAdapter.java new file mode 100644 index 0000000000..24240df9e1 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/asis/ManualHandlerAdapter.java @@ -0,0 +1,23 @@ +package webmvc.org.springframework.web.servlet.mvc.asis; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; +import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapter; +import webmvc.org.springframework.web.servlet.view.JspView; + +public class ManualHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(final Object handler) { + return handler instanceof Controller; + } + + @Override + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) + throws Exception { + final var controller = (Controller) handler; + final String viewName = controller.execute(request, response); + return new ModelAndView(new JspView(viewName)); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerAdapter.java new file mode 100644 index 0000000000..4d9b7a5d16 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerAdapter.java @@ -0,0 +1,20 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; + +public class AnnotationHandlerAdapter implements HandlerAdapter { + + @Override + public boolean supports(final Object handler) { + return handler instanceof HandlerExecution; + } + + @Override + public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) + throws Exception { + final var handlerExecution = (HandlerExecution) handler; + return handlerExecution.handle(request, response); + } +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java index d6cd9fef58..66c2dc7c23 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/AnnotationHandlerMapping.java @@ -12,7 +12,7 @@ import java.util.*; import java.util.stream.Collectors; -public class AnnotationHandlerMapping { +public class AnnotationHandlerMapping implements HandlerMapping { private static final Logger log = LoggerFactory.getLogger(AnnotationHandlerMapping.class); @@ -24,6 +24,7 @@ public AnnotationHandlerMapping(final Object... basePackage) { this.handlerExecutions = new HashMap<>(); } + @Override public void initialize() { final var reflections = new Reflections(basePackage); final Set> controllerClasses = reflections.getTypesAnnotatedWith(Controller.class); @@ -57,6 +58,7 @@ private void addHandlerExecution(final Method method, final RequestMapping reque } } + @Override public Object getHandler(final HttpServletRequest request) { final String requestURI = request.getRequestURI(); final var requestMethod = RequestMethod.valueOf(request.getMethod()); diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerAdapter.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerAdapter.java new file mode 100644 index 0000000000..8b69323798 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerAdapter.java @@ -0,0 +1,12 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import webmvc.org.springframework.web.servlet.ModelAndView; + +public interface HandlerAdapter { + + boolean supports(final Object handler); + + ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception; +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerMapping.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerMapping.java new file mode 100644 index 0000000000..d900bb88f5 --- /dev/null +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/mvc/tobe/HandlerMapping.java @@ -0,0 +1,10 @@ +package webmvc.org.springframework.web.servlet.mvc.tobe; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HandlerMapping { + + void initialize(); + + Object getHandler(final HttpServletRequest request); +} diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java index b42c3466f0..5f5da36e33 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JsonView.java @@ -8,7 +8,19 @@ public class JsonView implements View { + private final String viewName; + + public JsonView(final String viewName) { + this.viewName = viewName; + } + @Override public void render(final Map model, final HttpServletRequest request, HttpServletResponse response) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + public String getViewName() { + return viewName; } } diff --git a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java index 3f4cc906ff..4eb594e8bc 100644 --- a/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java +++ b/mvc/src/main/java/webmvc/org/springframework/web/servlet/view/JspView.java @@ -14,7 +14,10 @@ public class JspView implements View { public static final String REDIRECT_PREFIX = "redirect:"; + private final String viewName; + public JspView(final String viewName) { + this.viewName = viewName; } @Override @@ -28,4 +31,9 @@ public void render(final Map model, final HttpServletRequest request, // todo } + + @Override + public String getViewName() { + return viewName; + } }