Skip to content

Commit

Permalink
[mvc구현 / 2단계] 로이(김덕우) 미션 제출합니다. (#520)
Browse files Browse the repository at this point in the history
* feat: 핸들러 매핑 추상화

* feat: 핸들러 어댑터 인터페이스 및 구현체 생성

* feat: HandlerAdapterRegistry 및 HandlerMappingRegistry 구현

* feat: DispatcherServlet 내 핸들러매핑 및 핸들러어댑터 설정

* refactor: 메서드 파라미터 리팩터링
  • Loading branch information
the9kim authored Sep 22, 2023
1 parent cfb3e55 commit 9d8f40a
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 14 deletions.
34 changes: 28 additions & 6 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,37 @@
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.tobe.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapterRegistry;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMapping;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMappingRegistry;
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 HandlerMappingRegistry handlerMappingRegistry;
private HandlerAdapterRegistry handlerAdapterRegistry;

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

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

public void addHandlerMapping(HandlerMapping handlerMapping) {
handlerMappingRegistry.addHandlerMapping(handlerMapping);
}

public void addHandlerAdapter(HandlerAdapter handlerAdapter) {
handlerAdapterRegistry.addHandlerAdapter(handlerAdapter);
}

@Override
Expand All @@ -30,9 +45,16 @@ 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);
Object handler = handlerMappingRegistry.getHandler(request)
.orElse(new IllegalArgumentException("URI에 해당하는 핸들러가 존재하지 않습니다."));

HandlerAdapter handlerAdapter = handlerAdapterRegistry.getHandlerAdapter(handler);

ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);

JspView view = (JspView) modelAndView.getView();
move(view.getView(), request, response);

} catch (Throwable e) {
log.error("Exception : {}", e.getMessage(), e);
throw new ServletException(e.getMessage());
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/com/techcourse/DispatcherServletInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import web.org.springframework.web.WebApplicationInitializer;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.AnnotationHandlerMapping;

/**
* Base class for {@link WebApplicationInitializer}
Expand All @@ -19,7 +21,16 @@ public class DispatcherServletInitializer implements WebApplicationInitializer {
public void onStartup(final ServletContext servletContext) {
final var dispatcherServlet = new DispatcherServlet();

dispatcherServlet.addHandlerMapping(new AnnotationHandlerMapping("com.techcourse"));
dispatcherServlet.addHandlerMapping(new ManualHandlerMapping());

dispatcherServlet.addHandlerAdapter(new AnnotationHandlerAdapter());
dispatcherServlet.addHandlerAdapter(new ManualHandlerAdapter());

dispatcherServlet.init();

final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);

if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
Expand Down
24 changes: 24 additions & 0 deletions app/src/main/java/com/techcourse/ManualHandlerAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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.asis.Controller;
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 isSupport(Object handler) {
return handler instanceof Controller;
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Controller controller = (Controller) handler;
String viewName = controller.execute(request, response);
return new ModelAndView(new JspView(viewName));
}

}
10 changes: 7 additions & 3 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package com.techcourse;

import com.techcourse.controller.*;
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());
Expand All @@ -28,8 +31,9 @@ public void initialize() {
.forEach(path -> log.info("Path : {}, Controller : {}", path, controllers.get(path).getClass()));
}

public Controller getHandler(final String requestURI) {
log.debug("Request Mapping Uri : {}", requestURI);
@Override
public Object getHandler(final HttpServletRequest request) {
String requestURI = request.getRequestURI();
return controllers.get(requestURI);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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 isSupport(Object handler) {
return handler instanceof HandlerExecution;
}

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerExecution handlerExecution = (HandlerExecution) handler;
return handlerExecution.handle(request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import java.util.Map;
import java.util.Set;

public class AnnotationHandlerMapping {
public class AnnotationHandlerMapping implements HandlerMapping{

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

Expand All @@ -25,6 +25,7 @@ public AnnotationHandlerMapping(final Object... basePackage) {
this.handlerExecutions = new HashMap<>();
}

@Override
public void initialize() {
log.info("Initialized AnnotationHandlerMapping!");

Expand All @@ -46,18 +47,19 @@ private void mapHandlerForAnnotatedMethod(Method declaredMethod) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
RequestMapping annotation = declaredMethod.getAnnotation(RequestMapping.class);
RequestMethod[] method = annotation.method();
mapHandlerForRequestMethods(declaredMethod, annotation, method);
HandlerExecution handlerExecution = new HandlerExecution(declaredMethod);
mapHandlerForRequestMethods(declaredMethod, annotation, method, handlerExecution);
}
}

private void mapHandlerForRequestMethods(Method declaredMethod, RequestMapping annotation, RequestMethod[] method) {
private void mapHandlerForRequestMethods(Method declaredMethod, RequestMapping annotation, RequestMethod[] method, HandlerExecution handlerExecution) {
for (RequestMethod requestMethod : method) {
HandlerKey handlerKey = new HandlerKey(annotation.value(), requestMethod);
HandlerExecution handlerExecution = new HandlerExecution(declaredMethod);
handlerExecutions.put(handlerKey, handlerExecution);
}
}

@Override
public Object getHandler(final HttpServletRequest request) {
String requestURI = request.getRequestURI();
String method = 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 isSupport(Object handler);

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

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

public class HandlerAdapterRegistry {

private final List<HandlerAdapter> handlerAdapters;

public HandlerAdapterRegistry() {
this.handlerAdapters = new ArrayList<>();
}

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

public HandlerAdapter getHandlerAdapter(Object handler) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.isSupport(handler)) {
return handlerAdapter;
}
}
throw new IllegalArgumentException("해당 handler를 찾을 수 없습니다.");
}
}
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(HttpServletRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

import jakarta.servlet.http.HttpServletRequest;

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

public class HandlerMappingRegistry {

private final List<HandlerMapping> handlerMappings;

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

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

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

public Optional<Object> getHandler(HttpServletRequest request) {
return handlerMappings.stream()
.map(handlerMapping -> handlerMapping.getHandler(request))
.filter(Objects::nonNull)
.findFirst();
}
}
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:";

public JspView(final String viewName) {
private final String view;

public JspView(String view) {
this.view = view;
}

@Override
Expand All @@ -28,4 +31,8 @@ public void render(final Map<String, ?> model, final HttpServletRequest request,

// todo
}

public String getView() {
return view;
}
}

0 comments on commit 9d8f40a

Please sign in to comment.