Skip to content

Commit

Permalink
[MVC 구현하기 - 2단계] 제이미(임정수) 미션 제출합니다. (#492)
Browse files Browse the repository at this point in the history
* feat: Legacy MVC와 @MVC 통합하기

* feat: url을 찾을 수 없는 경우의 예외 처리 추가

* refactor: 메서드 명을 명확하게 수정

* refactor: handler의 인스턴스를 주입 받도록 수정

* refactor: static 제거

* refactor: 기존 코드로 돌려 놓기

* refactor: 특정 컨트롤러만 어노테이션 기반 컨트롤러로 변경

* feat: 핸들러 어뎁터 추가

* feat: 핸들러 매핑 추가

* feat: DispatcherServlet과의 연결

* rename: 패키지 위치 변경

* refacotr: map의 getOrDefault()를 사용하도록 수정
  • Loading branch information
JJ503 authored Sep 24, 2023
1 parent 574c088 commit 7347ffc
Show file tree
Hide file tree
Showing 18 changed files with 240 additions and 66 deletions.
32 changes: 14 additions & 18 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import webmvc.org.springframework.web.servlet.view.JspView;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.mvc.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.HandlerAdapterRegistry;
import webmvc.org.springframework.web.servlet.mvc.HandlerMappingRegistry;

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 HandlerAdapterRegistry handlerAdapterRegistry;
private final HandlerMappingRegistry handlerMappingRegistry;

public DispatcherServlet() {
public DispatcherServlet(final HandlerAdapterRegistry handlerAdapterRegistry, final HandlerMappingRegistry handlerMappingRegistry) {
this.handlerAdapterRegistry = handlerAdapterRegistry;
this.handlerMappingRegistry = handlerMappingRegistry;
}

@Override
public void init() {
manualHandlerMapping = new ManualHandlerMapping();
manualHandlerMapping.initialize();
handlerMappingRegistry.initialize();
}

@Override
Expand All @@ -30,22 +35,13 @@ protected void service(final HttpServletRequest request, final HttpServletRespon
log.debug("Method : {}, Request URI : {}", request.getMethod(), requestURI);

try {
final var controller = manualHandlerMapping.getHandler(requestURI);
final var viewName = controller.execute(request, response);
move(viewName, request, response);
final Object handle = handlerMappingRegistry.getHandler(request);
final HandlerAdapter handlerAdapter = handlerAdapterRegistry.getHandlerAdapter(handle);
final ModelAndView modelAndView = handlerAdapter.handle(handle, request, response);
modelAndView.getView().render(modelAndView.getModel(), 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 {
if (viewName.startsWith(JspView.REDIRECT_PREFIX)) {
response.sendRedirect(viewName.substring(JspView.REDIRECT_PREFIX.length()));
return;
}

final var requestDispatcher = request.getRequestDispatcher(viewName);
requestDispatcher.forward(request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.WebApplicationInitializer;
import webmvc.org.springframework.web.servlet.mvc.HandlerAdapterRegistry;
import webmvc.org.springframework.web.servlet.mvc.HandlerMappingRegistry;
import webmvc.org.springframework.web.servlet.mvc.asis.ControllerHandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecutionHandlerAdapter;

/**
* Base class for {@link WebApplicationInitializer}
Expand All @@ -17,7 +22,10 @@ public class DispatcherServletInitializer implements WebApplicationInitializer {

@Override
public void onStartup(final ServletContext servletContext) {
final var dispatcherServlet = new DispatcherServlet();
final HandlerAdapterRegistry handlerAdapterRegistry = initializedHandlerAdapterRegistry();
final HandlerMappingRegistry handlerMappingRegistry = initializedHandlerAdapater();

final var dispatcherServlet = new DispatcherServlet(handlerAdapterRegistry, handlerMappingRegistry);

final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);
if (registration == null) {
Expand All @@ -30,4 +38,20 @@ public void onStartup(final ServletContext servletContext) {

log.info("Start AppWebApplication Initializer");
}

private static HandlerMappingRegistry initializedHandlerAdapater() {
final HandlerMappingRegistry handlerMappingRegistry = new HandlerMappingRegistry();
handlerMappingRegistry.addHandlerMapping(new AnnotationHandlerMapping("com.techcourse.controller"));
handlerMappingRegistry.addHandlerMapping(new ManualHandlerMapping());

return handlerMappingRegistry;
}

private static HandlerAdapterRegistry initializedHandlerAdapterRegistry() {
final HandlerAdapterRegistry handlerAdapterRegistry = new HandlerAdapterRegistry();
handlerAdapterRegistry.addHandlerAdapter(new HandlerExecutionHandlerAdapter());
handlerAdapterRegistry.addHandlerAdapter(new ControllerHandlerAdapter());

return handlerAdapterRegistry;
}
}
20 changes: 13 additions & 7 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
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.HandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.asis.ForwardController;

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<>();
private 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 Object getHandler(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
log.debug("Request Mapping Uri : {}", requestURI);
return controllers.get(requestURI);

return controllers.getOrDefault(requestURI, new ForwardController("/404.jsp"));
}
}
10 changes: 5 additions & 5 deletions app/src/main/java/com/techcourse/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public String execute(final HttpServletRequest req, final HttpServletResponse re
}

return InMemoryUserRepository.findByAccount(req.getParameter("account"))
.map(user -> {
log.info("User : {}", user);
return login(req, user);
})
.orElse("redirect:/401.jsp");
.map(user -> {
log.info("User : {}", user);
return login(req, user);
})
.orElse("redirect:/401.jsp");
}

private String login(final HttpServletRequest request, final User user) {
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 register(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
final var user = new User(2,
req.getParameter("account"),
req.getParameter("password"),
req.getParameter("email"));
InMemoryUserRepository.save(user);

return "redirect:/index.jsp";
return new ModelAndView(new JspView("/index.jsp"));
}

@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public ModelAndView showRegister(final HttpServletRequest req, final HttpServletResponse res) throws Exception {
return new ModelAndView(new JspView("/register.jsp"));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package webmvc.org.springframework.web.servlet.mvc;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import webmvc.org.springframework.web.servlet.ModelAndView;

public interface HandlerAdapter {

boolean isSupport(final Object object);

ModelAndView handle(final Object object, final HttpServletRequest request, final HttpServletResponse response) throws Exception;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package webmvc.org.springframework.web.servlet.mvc;

import webmvc.org.springframework.web.servlet.mvc.exception.NotFoundHandlerAdapter;

import java.util.ArrayList;
import java.util.List;

public class HandlerAdapterRegistry {

final private List<HandlerAdapter> handlerAdapters = new ArrayList<>();

public void addHandlerAdapter(final HandlerAdapter handlerAdapter) {
handlerAdapters.add(handlerAdapter);
}

public HandlerAdapter getHandlerAdapter(final Object object) {
return handlerAdapters.stream()
.filter(handlerAdapter -> handlerAdapter.isSupport(object))
.findFirst()
.orElseThrow(() -> new NotFoundHandlerAdapter("해당 핸들러 어댑터를 찾지 못했습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package webmvc.org.springframework.web.servlet.mvc;

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
@@ -0,0 +1,29 @@
package webmvc.org.springframework.web.servlet.mvc;

import jakarta.servlet.http.HttpServletRequest;
import webmvc.org.springframework.web.servlet.mvc.exception.NotFoundHandlerMapping;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class HandlerMappingRegistry {

final private List<HandlerMapping> handlerMappings = new ArrayList<>();

public void initialize() {
handlerMappings.forEach(HandlerMapping::initialize);
}

public void addHandlerMapping(final HandlerMapping handlerMapping) {
handlerMappings.add(handlerMapping);
}

public Object getHandler(HttpServletRequest request) {
return handlerMappings.stream()
.map(handlerMapping -> handlerMapping.getHandler(request))
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> new NotFoundHandlerMapping("해당 핸들러 매핑을 찾지 못했습니다."));
}
}
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.HandlerAdapter;
import webmvc.org.springframework.web.servlet.view.JspView;

public class ControllerHandlerAdapter implements HandlerAdapter {

@Override
public boolean isSupport(final Object object) {
return object instanceof Controller;
}

@Override
public ModelAndView handle(final Object object, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final Controller controller = (Controller) object;
final String path = controller.execute(request, response);

return new ModelAndView(new JspView(path));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package webmvc.org.springframework.web.servlet.mvc.exception;

public class NotFoundHandlerAdapter extends IllegalArgumentException {

public NotFoundHandlerAdapter(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package webmvc.org.springframework.web.servlet.mvc.exception;

public class NotFoundHandlerMapping extends IllegalArgumentException {

public NotFoundHandlerMapping(final String message) {
super(message);
}
}
Loading

0 comments on commit 7347ffc

Please sign in to comment.