-
Notifications
You must be signed in to change notification settings - Fork 305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MVC 구현하기 - 2단계] 후디(조동현) 미션 제출합니다. #256
Changes from all commits
2c3c46f
16e9bc4
d779d20
ddcb8ed
89d1e61
bc3561c
8600d06
24ab02e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,13 +4,7 @@ | |
import jakarta.servlet.http.HttpServlet; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import nextstep.mvc.controller.asis.Controller; | ||
import nextstep.mvc.controller.tobe.HandlerExecution; | ||
import nextstep.mvc.view.JspView; | ||
import nextstep.mvc.controller.tobe.exception.ControllerNotFoundException; | ||
import nextstep.mvc.view.ModelAndView; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -20,19 +14,24 @@ public class DispatcherServlet extends HttpServlet { | |
private static final long serialVersionUID = 1L; | ||
private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class); | ||
|
||
private final List<HandlerMapping> handlerMappings; | ||
private final HandlerAdapterRegistry handlerAdapterRegistry; | ||
private final HandlerMappingRegistry handlerMappingRegistry; | ||
|
||
public DispatcherServlet() { | ||
this.handlerMappings = new ArrayList<>(); | ||
this.handlerAdapterRegistry = new HandlerAdapterRegistry(); | ||
this.handlerMappingRegistry = new HandlerMappingRegistry(); | ||
} | ||
|
||
@Override | ||
public void init() { | ||
handlerMappings.forEach(HandlerMapping::initialize); | ||
} | ||
|
||
public void addHandlerAdapter(final HandlerAdapter handlerAdapter) { | ||
handlerAdapterRegistry.addHandlerAdapter(handlerAdapter); | ||
} | ||
|
||
public void addHandlerMapping(final HandlerMapping handlerMapping) { | ||
handlerMappings.add(handlerMapping); | ||
handlerMappingRegistry.addHandlerMapping(handlerMapping); | ||
} | ||
|
||
@Override | ||
|
@@ -41,41 +40,15 @@ protected void service(final HttpServletRequest request, final HttpServletRespon | |
log.debug("Method : {}, Request URI : {}", request.getMethod(), request.getRequestURI()); | ||
|
||
try { | ||
final Object controller = getController(request); | ||
|
||
// TODO: 다형성을 통해 해결해야함 | ||
if (controller instanceof HandlerExecution) { | ||
HandlerExecution handlerExecution = (HandlerExecution) controller; | ||
ModelAndView modelAndView = handlerExecution.handle(request, response); | ||
Map<String, Object> model = modelAndView.getModel(); | ||
modelAndView.getView().render(model, request, response); | ||
return; | ||
} | ||
Object controller = handlerMappingRegistry.getHandler(request) | ||
.orElseThrow(ControllerNotFoundException::new); | ||
|
||
final var viewName = ((Controller) controller).execute(request, response); | ||
move(viewName, request, response); | ||
HandlerAdapter handlerAdapter = handlerAdapterRegistry.getHandlerAdapter(controller); | ||
ModelAndView modelAndView = handlerAdapter.handle(request, response, controller); | ||
modelAndView.render(request, response); | ||
} catch (Throwable e) { | ||
log.error("Exception : {}", e.getMessage(), e); | ||
throw new ServletException(e.getMessage()); | ||
} | ||
} | ||
|
||
private Object getController(final HttpServletRequest request) { | ||
return handlerMappings.stream() | ||
.map(handlerMapping -> handlerMapping.getHandler(request)) | ||
.filter(Objects::nonNull) | ||
.findFirst() | ||
.orElseThrow(); | ||
} | ||
|
||
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; | ||
} | ||
Comment on lines
-71
to
-76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이번 PR에서 로직을 모두 옮겼습니다 😄 |
||
|
||
final var requestDispatcher = request.getRequestDispatcher(viewName); | ||
requestDispatcher.forward(request, response); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package nextstep.mvc; | ||
|
||
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(final HandlerAdapter handlerAdapter) { | ||
handlerAdapters.add(handlerAdapter); | ||
} | ||
|
||
public HandlerAdapter getHandlerAdapter(final Object handler) { | ||
return handlerAdapters.stream() | ||
.filter(it -> it.supports(handler)) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("지원하지 않는 핸들러입니다.")); | ||
// TODO: 적절한 예외로 변경 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package nextstep.mvc; | ||
|
||
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() { | ||
handlerMappings = new ArrayList<>(); | ||
} | ||
|
||
public void addHandlerMapping(final HandlerMapping handlerMapping) { | ||
handlerMapping.initialize(); | ||
handlerMappings.add(handlerMapping); | ||
} | ||
|
||
public Optional<Object> getHandler(final HttpServletRequest request) { | ||
return handlerMappings.stream() | ||
.map(handlerMapping -> handlerMapping.getHandler(request)) | ||
.filter(Objects::nonNull) | ||
.findFirst(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package nextstep.mvc.controller.asis; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import nextstep.mvc.HandlerAdapter; | ||
import nextstep.mvc.view.JspView; | ||
import nextstep.mvc.view.ModelAndView; | ||
|
||
public class ControllerHandlerAdapter 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 { | ||
String viewName = ((Controller) handler).execute(request, response); | ||
JspView jspView = new JspView(viewName); | ||
return new ModelAndView(jspView); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,14 +6,9 @@ | |
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import nextstep.mvc.HandlerMapping; | ||
import nextstep.mvc.controller.tobe.exception.ControllerNotFoundException; | ||
import nextstep.web.annotation.Controller; | ||
import nextstep.web.annotation.RequestMapping; | ||
import org.reflections.Reflections; | ||
import org.reflections.scanners.Scanners; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
@@ -30,8 +25,9 @@ public AnnotationHandlerMapping(final Object... basePackage) { | |
} | ||
|
||
public void initialize() { | ||
Set<Class<?>> controllerClasses = extractClasses(); | ||
List<Method> methods = extractMethods(controllerClasses); | ||
ControllerScanner controllerScanner = new ControllerScanner(); | ||
Map<Class<?>, Object> controllers = controllerScanner.getControllers(basePackage); | ||
List<Method> methods = extractMethods(controllers); | ||
|
||
for (Method method : methods) { | ||
addHandlerExecutions(method); | ||
|
@@ -40,13 +36,8 @@ public void initialize() { | |
log.info("Initialized AnnotationHandlerMapping!"); | ||
} | ||
|
||
private Set<Class<?>> extractClasses() { | ||
Reflections classReflections = new Reflections(basePackage, Scanners.TypesAnnotated); | ||
return classReflections.getTypesAnnotatedWith(Controller.class); | ||
} | ||
|
||
private List<Method> extractMethods(final Set<Class<?>> controllers) { | ||
return controllers.stream() | ||
private List<Method> extractMethods(final Map<Class<?>, Object> controllers) { | ||
return controllers.keySet().stream() | ||
.flatMap(it -> Arrays.stream(it.getMethods())) | ||
.filter(it -> it.isAnnotationPresent(RequestMapping.class)) | ||
.collect(Collectors.toList()); | ||
|
@@ -62,14 +53,8 @@ private void addHandlerExecutions(final Method method) { | |
} | ||
} | ||
|
||
public Object getHandler(final HttpServletRequest request) { | ||
public HandlerExecution getHandler(final HttpServletRequest request) { | ||
HandlerKey handlerKey = new HandlerKey(request); | ||
HandlerExecution handlerExecution = handlerExecutions.get(handlerKey); | ||
|
||
if (handlerExecution == null) { | ||
throw new ControllerNotFoundException(); | ||
} | ||
|
||
return handlerExecution; | ||
return handlerExecutions.get(handlerKey); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
남겨주신 부분이 맞는 것 같습니다! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package nextstep.mvc.controller.tobe; | ||
|
||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import nextstep.web.annotation.Controller; | ||
import org.reflections.Reflections; | ||
import org.reflections.scanners.Scanners; | ||
|
||
public class ControllerScanner { | ||
|
||
public Map<Class<?>, Object> getControllers(final Object[] basePackage) { | ||
Reflections classReflections = new Reflections(basePackage, Scanners.TypesAnnotated); | ||
Set<Class<?>> classes = classReflections.getTypesAnnotatedWith(Controller.class); | ||
|
||
return instantiateControllers(classes); | ||
} | ||
|
||
private Map<Class<?>, Object> instantiateControllers(final Set<Class<?>> classes) { | ||
return classes.stream() | ||
.collect(Collectors.toMap(clazz -> clazz, this::instantiateClass)); | ||
} | ||
|
||
private Object instantiateClass(final Class<?> clazz) { | ||
try { | ||
return clazz.getConstructor().newInstance(); | ||
} catch (ReflectiveOperationException e) { | ||
throw new IllegalArgumentException("Controller를 인스턴스화 할 수 없습니다."); | ||
// TODO: 적절한 예외를 던져야함 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package nextstep.mvc.controller.tobe; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import nextstep.mvc.HandlerAdapter; | ||
import nextstep.mvc.view.ModelAndView; | ||
|
||
public class HandlerExecutionHandlerAdapter 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 { | ||
return ((HandlerExecution) handler).handle(request, response); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분도 /register/view가 아닌 /register로 변경하는 것이 어떨까요? ㅎㅎ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3단계 미션에서 반영했습니다! :)