Skip to content

Commit

Permalink
[MVC 구현하기 2단계] 호이(이건호) 미션 제출합니다. (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
This2sho authored Sep 24, 2023
1 parent 98d7efc commit 698aea9
Show file tree
Hide file tree
Showing 14 changed files with 227 additions and 76 deletions.
34 changes: 15 additions & 19 deletions app/src/main/java/com/techcourse/DispatcherServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,42 @@
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.mvc.tobe.HandlerAdapter;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerAdapters;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMappings;

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 HandlerAdapters handlerAdapters;

public DispatcherServlet() {
public DispatcherServlet(final HandlerMappings handlerMappings, final HandlerAdapters handlerAdapters) {
this.handlerMappings = handlerMappings;
this.handlerAdapters = handlerAdapters;
}

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

@Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException {
protected void service(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException {
final String requestURI = request.getRequestURI();
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 handler = handlerMappings.getHandler(request);
final HandlerAdapter handlerAdapter = handlerAdapters.getHandlerAdapter(handler);

handlerAdapter.execute(request, response, handler);
} 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.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.mvc.tobe.HandlerAdapters;
import webmvc.org.springframework.web.servlet.mvc.tobe.HandlerMappings;

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

@Override
public void onStartup(final ServletContext servletContext) {
final var dispatcherServlet = new DispatcherServlet();
final HandlerMappings handlerMappings = new HandlerMappings();
handlerMappings.add(new ManualHandlerMapping());
handlerMappings.add(new AnnotationHandlerMapping());

final HandlerAdapters handlerAdapters = new HandlerAdapters();
handlerAdapters.add(new ManualHandlerAdapter());
handlerAdapters.add(new AnnotationHandlerAdapter());

final var dispatcherServlet = new DispatcherServlet(handlerMappings, handlerAdapters);

final var registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, dispatcherServlet);
if (registration == null) {
Expand Down
22 changes: 13 additions & 9 deletions app/src/main/java/com/techcourse/ManualHandlerMapping.java
Original file line number Diff line number Diff line change
@@ -1,35 +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 java.util.HashMap;
import java.util.Map;
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;

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) {
log.debug("Request Mapping Uri : {}", requestURI);
return controllers.get(requestURI);
@Override
public Controller getHandler(final HttpServletRequest request) {
log.debug("Request Mapping Uri : {}", request.getRequestURI());
return controllers.get(request.getRequestURI());
}
}
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(HttpServletRequest req, HttpServletResponse res) {
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", method = RequestMethod.GET)
public ModelAndView show(HttpServletRequest req, HttpServletResponse res) {
return new ModelAndView(new JspView("/register.jsp"));
}
}
2 changes: 1 addition & 1 deletion app/src/main/webapp/login.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small"><a href="/register/view">아이디가 없나요? 회원가입 하러가기</a></div>
<div class="small"><a href="/register">아이디가 없나요? 회원가입 하러가기</a></div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package webmvc.org.springframework.web.servlet.mvc.asis;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
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 void execute(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
throws Exception {
final Controller controller = (Controller) handler;
final String viewName = controller.execute(request, response);
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
@@ -0,0 +1,25 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
import webmvc.org.springframework.web.servlet.ModelAndView;
import webmvc.org.springframework.web.servlet.View;

public class AnnotationHandlerAdapter implements HandlerAdapter {

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

@Override
public void execute(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
throws Exception {
final HandlerExecution execution = (HandlerExecution) handler;
final ModelAndView mav = execution.handle(request, response);
final Map<String, Object> model = mav.getModel();
final View view = mav.getView();
view.render(model, request, response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@

import context.org.springframework.stereotype.Controller;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Set;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import web.org.springframework.web.bind.annotation.RequestMapping;
import web.org.springframework.web.bind.annotation.RequestMethod;

public class AnnotationHandlerMapping {
public class AnnotationHandlerMapping implements HandlerMapping {

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

Expand All @@ -28,54 +26,48 @@ public AnnotationHandlerMapping(final Object... basePackage) {
this.handlerExecutions = new HashMap<>();
}

@Override
public void initialize() {
for (Object subPackage : basePackage) {
intTargetPackage(subPackage);
initTargetPackage(subPackage);
}
log.info("Initialized AnnotationHandlerMapping!");
}

private void intTargetPackage(final Object base) {
private void initTargetPackage(final Object base) {
final Reflections reflections = new Reflections(base);
final Set<Class<?>> controllers = reflections.getTypesAnnotatedWith(Controller.class);
for (Class<?> clazz : controllers) {
initTargetController(clazz);
}
reflections.getTypesAnnotatedWith(Controller.class)
.forEach(this::initTargetController);
}

private void initTargetController(final Class<?> clazz) {
final Method[] methods = clazz.getMethods();
for (Method method : methods) {
initTargetMethod(clazz, method);
initTargetMethod(method);
}
}

private void initTargetMethod(final Class<?> clazz, final Method method) {
if (Objects.nonNull(method.getDeclaredAnnotation(RequestMapping.class))) {
final HandlerKey key = getHandlerKey(method);
final Object instance = getInstance(clazz);
final HandlerExecution handlerExecution = new HandlerExecution(instance, method);
handlerExecutions.put(key, handlerExecution);
private void initTargetMethod(final Method method) {
if (method.isAnnotationPresent(RequestMapping.class)) {
final List<HandlerKey> keys = getHandlerKeys(method);
final HandlerExecution handlerExecution = new HandlerExecution(method);

final Map<HandlerKey, HandlerExecution> handlerExecutionMap = keys.stream()
.map(key -> Map.entry(key, handlerExecution))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
handlerExecutions.putAll(handlerExecutionMap);
}
}

private HandlerKey getHandlerKey(final Method method) {
private List<HandlerKey> getHandlerKeys(final Method method) {
final RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
final String uri = requestMapping.value();
final RequestMethod requestMethod = requestMapping.method()[0];
return new HandlerKey(uri, requestMethod);
}

private Object getInstance(final Class<?> clazz) {
try {
final Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e){
throw new RuntimeException(e.getMessage());
}
return Arrays.stream(requestMapping.method())
.map(requestMethod -> new HandlerKey(uri, requestMethod))
.collect(Collectors.toUnmodifiableList());
}

@Override
public Object getHandler(final HttpServletRequest request) {
final String requestURI = request.getRequestURI();
final String method = request.getMethod();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package webmvc.org.springframework.web.servlet.mvc.tobe;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

public interface HandlerAdapter {

boolean supports(Object handler);

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

import java.util.HashSet;
import java.util.Set;

public class HandlerAdapters {

private Set<HandlerAdapter> values = new HashSet<>();

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

public HandlerAdapter getHandlerAdapter(Object handler) {
return values.stream()
.filter(adapter -> adapter.supports(handler))
.findAny()
.orElseThrow();
}
}
Loading

0 comments on commit 698aea9

Please sign in to comment.