Skip to content

Commit

Permalink
[ MVC 구현하기 - 2 단계 ] 필립(양재필) 미션 제출합니다. (#453)
Browse files Browse the repository at this point in the history
* refactor: 컨트롤러 초기화 메소드 추출

* chore: 컨트롤러 로딩시 로깅 추가

* feat: 어노테이션 기반 컨트롤러 맵핑 기능 추가

* refactor: RegisterController를 어노테이션 기반으로 변경

- 기존 RegisterController를 controller implement기반에서annotation기반으로 변경
- RegisterViewController를 RegisterController로 통합

* refactor: HandlerMapping 추가

- DispatcherServlet에서 각각의 HandlerMapping을 가지지 않도록 HanlderMappings 추가
- AnnotationHanlderMapping, ManualHandlerMapping의 HandlerMapping interface 추가
- HandlerMapping추가 위치 이동 : DispatcheServlet -> DispatcheServletInitalizer

* refactor: HandlerAdapter 적용

- ManualControllerHandlerAdatper
  - 기존 Controller inteface를 위한 어댑터
  - Controller 객체를 필드로 가지고있음
- HandlerExcutionAdapter
  - Annotation 기반으로 생성된 HanlderExcution을 위한 어댑터
  - HandlerExcution을 가지고 있음
  • Loading branch information
pilyang authored Sep 19, 2023
1 parent e341199 commit 5d8ac5e
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 57 deletions.
49 changes: 31 additions & 18 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,59 @@
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.View;
import webmvc.org.springframework.web.servlet.mvc.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.HandlerAdapterFactory;
import webmvc.org.springframework.web.servlet.mvc.HandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.HandlerMappings;

import java.util.Map;

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 HandlerMappings handlerMappings;
private final HandlerAdapterFactory handlerAdapterFactory;

public DispatcherServlet() {
this.handlerMappings = new HandlerMappings();
this.handlerAdapterFactory = new HandlerAdapterFactory();
}

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

public void addHandlerAdapterType(final Class<?> handlerType, final Class<? extends HandlerAdapter> adapterType) {
handlerAdapterFactory.addAdapterType(handlerType, adapterType);
}

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

@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);
} catch (Throwable e) {
final Object handler = handlerMappings.getHandler(request);
final HandlerAdapter adapter = handlerAdapterFactory.getAdapter(handler);
final ModelAndView modelAndView = adapter.handle(request, response);
render(modelAndView, request, response);
} catch (Exception 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);
private void render(final ModelAndView modelAndView, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final View view = modelAndView.getView();
final Map<String, Object> model = modelAndView.getModel();
view.render(model, request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.WebApplicationInitializer;
import webmvc.org.springframework.web.servlet.mvc.HandlerExecutionAdapter;
import webmvc.org.springframework.web.servlet.mvc.asis.Controller;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerExecution;

/**
* Base class for {@link WebApplicationInitializer}
Expand All @@ -14,10 +18,15 @@ public class DispatcherServletInitializer implements WebApplicationInitializer {
private static final Logger log = LoggerFactory.getLogger(DispatcherServletInitializer.class);

private static final String DEFAULT_SERVLET_NAME = "dispatcher";
private static final String PACKAGE_TO_SEARCH_CONTROLLER = "com.techcourse";

@Override
public void onStartup(final ServletContext servletContext) {
final var dispatcherServlet = new DispatcherServlet();
dispatcherServlet.addHandlerMapping(new ManualHandlerMapping());
dispatcherServlet.addHandlerMapping(new AnnotationHandlerMapping(PACKAGE_TO_SEARCH_CONTROLLER));
dispatcherServlet.addHandlerAdapterType(HandlerExecution.class, HandlerExecutionAdapter.class);
dispatcherServlet.addHandlerAdapterType(Controller.class, ManualControllerHandlerAdapter.class);

final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);
if (registration == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.techcourse;

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.mvc.asis.Controller;
import webmvc.org.springframework.web.servlet.view.JspView;

public class ManualControllerHandlerAdapter implements HandlerAdapter {

private final Controller controller;

public ManualControllerHandlerAdapter(final Object controller) {
this.controller = (Controller) controller;
}

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

@Override
public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
final String path = controller.execute(request, response);
return new ModelAndView(new JspView(path));
}
}
20 changes: 15 additions & 5 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
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<>();

@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 boolean isSupport(HttpServletRequest request) {
return controllers.containsKey(request.getRequestURI());
}

@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,29 @@

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(HttpServletRequest req, HttpServletResponse res) {
final var user = new User(2,
req.getParameter("account"),
req.getParameter("password"),
req.getParameter("email"));
InMemoryUserRepository.save(user);
return new ModelAndView(new JspView("redirect:/index.jsp"));
}

return "redirect:/index.jsp";
@RequestMapping(value = "/register/view", method = RequestMethod.GET)
public ModelAndView show(HttpServletRequest req, HttpServletResponse res) {
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(Object handler);

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class HandlerAdapterFactory {

private static final Logger log = LoggerFactory.getLogger(HandlerExecutionAdapter.class);

private final Map<Class<?>, Class<? extends HandlerAdapter>> supportedHandlerAdapters;
private final Map<Object, HandlerAdapter> adapterCache;

public HandlerAdapterFactory() {
this.supportedHandlerAdapters = new HashMap<>();
this.adapterCache = new ConcurrentHashMap<>();
}

public void addAdapterType(final Class<?> handlerType, final Class<? extends HandlerAdapter> adapterType) {
log.info("Add handler adapter - {}", adapterType.getSimpleName());
supportedHandlerAdapters.put(handlerType, adapterType);
}

public HandlerAdapter getAdapter(final Object handler) {
if (!adapterCache.containsKey(handler)) {
adapterCache.put(handler, makeHandlerAdapter(handler));
}
return adapterCache.get(handler);
}

private HandlerAdapter makeHandlerAdapter(final Object handler) {
final Class<? extends HandlerAdapter> adapterClass = supportedHandlerAdapters.entrySet()
.stream()
.filter(entry -> entry.getKey().isInstance(handler))
.map(Map.Entry::getValue)
.findAny()
.orElseThrow(() -> new RuntimeException("Could not find supported adapter for " + handler.getClass().getSimpleName()));

try {
return adapterClass.getDeclaredConstructor(Object.class).newInstance(handler);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package webmvc.org.springframework.web.servlet.mvc;

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.HandlerExecution;

public class HandlerExecutionAdapter implements HandlerAdapter {

private final HandlerExecution handlerExecution;

public HandlerExecutionAdapter(final Object handlerExecution) {
this.handlerExecution = (HandlerExecution) handlerExecution;
}

@Override
public boolean isSupport(Object handler) {
return handler instanceof HandlerExecution;
}

@Override
public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response) throws Exception {
return handlerExecution.handle(request, response);
}
}
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;

public interface HandlerMapping {

void initialize();

boolean isSupport(HttpServletRequest request);

Object getHandler(HttpServletRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package webmvc.org.springframework.web.servlet.mvc;

import jakarta.servlet.http.HttpServletRequest;

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

public class HandlerMappings {

private final List<HandlerMapping> handlerMappings;

public HandlerMappings() {
this.handlerMappings = new ArrayList<>();
}

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

public void initialize() {
for (final HandlerMapping handlerMapping : handlerMappings) {
handlerMapping.initialize();
}
}

public Object getHandler(final HttpServletRequest request) {
final HandlerMapping handlerMapping = handlerMappings.stream()
.filter(h -> h.isSupport(request))
.findAny()
.orElseThrow(() -> new RuntimeException("Could not find supporting hanlder for " + request.getMethod() + " " + request.getRequestURI()));
return handlerMapping.getHandler(request);
}
}
Loading

0 comments on commit 5d8ac5e

Please sign in to comment.