Skip to content

Commit

Permalink
[MVC 구현하기 - 2단계] 망고(고재철) 미션 제출합니다. (#531)
Browse files Browse the repository at this point in the history
* feat: HandlerMapping 인터페이스 구현

* feat: HandlerAdapter 인터페이스 및 ManualHandlerAdapter, AnnotationHandlerAdapter 구현

* feat: HandlerMappings, HandlerAdapters 구현

* feat: 인터페이스 기반, 어노테이션 기반 동시 지원하도록 DispatcherServlet 구현

* refactor: 어노테이션 방식으로 RegisterController 변경

* refactor: RegisterController URI 매핑 수정

* refactor: Override 어노테이션 추가
  • Loading branch information
Go-Jaecheol authored Sep 24, 2023
1 parent 223e029 commit cd71121
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 36 deletions.
28 changes: 19 additions & 9 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,50 @@
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 {

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;
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/techcourse/HandlerAdapters.java
Original file line number Diff line number Diff line change
@@ -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<HandlerAdapter> 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);
}
}
30 changes: 30 additions & 0 deletions app/src/main/java/com/techcourse/HandlerMappings.java
Original file line number Diff line number Diff line change
@@ -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<HandlerMapping> 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);
}
}
15 changes: 10 additions & 5 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -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<String, Controller> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@

public interface View {
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

String getViewName();
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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<Class<?>> controllerClasses = reflections.getTypesAnnotatedWith(Controller.class);
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, ?> model, final HttpServletRequest request, HttpServletResponse response) throws Exception {
throw new UnsupportedOperationException();
}

@Override
public String getViewName() {
return viewName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,4 +31,9 @@ public void render(final Map<String, ?> model, final HttpServletRequest request,

// todo
}

@Override
public String getViewName() {
return viewName;
}
}

0 comments on commit cd71121

Please sign in to comment.